1. 程式人生 > >java實現下載網路伺服器上的附件/圖片到本地

java實現下載網路伺服器上的附件/圖片到本地

新人小白一枚,記錄下工作中遇到的一點問題和解決方案。僅供自己以後複習參考之用。歡迎大神指導,交流。

剛開始學寫部落格,不好的地方請委婉的指出來哦。

先來說說需求背景

使用java來實現從網站網頁獲取內容(抓取網頁)。可能會遇到以下兩種情況(我暫時就只是遇到了這兩種情況)。

1、java後臺程式可以通過外網直接下載

這個就不做過多的論述,直接原樣抓取過來,後臺通過java中的DOM操作,批量的將a標籤中的href屬性和img標籤中src屬性修改為絕對路徑即可,直接下載或者訪問。

2、java後臺程式不能通過外網直接下載

這裡有存在兩種情況:第一種:通過瀏覽器直接訪問網站,可以正常下載,但是通過java後臺程式碼跳轉訪問卻不能下載附件/圖片。出現這種情況的原因是網站對不能下載的相關內容添加了防盜鏈。第二種:通過瀏覽器不能直接訪問,需要通過VPN / 伺服器代理 等方式,才能訪問的內網資源網站。以上兩種情況就是本文討論的重點,可以通過接下來即將介紹的一種方式統一解決。

實際業務需求

一般的訪問形式是這樣的,存在兩臺伺服器,伺服器A是某種組織機構的,是內網才能訪問,伺服器B另一家組織機構的是外網可以直接訪問的。現在兩家要要合作,需要實現的是:使用者可以通過伺服器B訪問伺服器A中指定的網站(該網站具有登入驗證功能)。注:B能訪問到A,需要A將B的網路地址新增到白名單。當用戶通過B登入A的網站時,需要我們在後臺記錄下cookie、header等一系列驗證引數,並將它們新增到訊息頭髮送給A的伺服器,驗證通過即可正常訪問該網站。但是a標籤中hre連結和img中src連線,卻不能直接傳入相關引數,這就會導致訪問失敗。不能顯示圖片和下載相關附件。解決:將不能直接獲取到的內容,先下載到伺服器B,再將不能獲取到內容的相關連結替換為下載到B上資源的連結。當用戶需要下載時,就是直接從B上獲取資源下載。

功能實現程式碼

/**
 * 儲存附件到本地並返回本地檔名
 * @param fileUrl 附件網路地址
 * @param suffix 附件字尾名
 * @return 本地附件名稱
 */
public String getLocalFileUrl(String fileUrl, String suffix ){
	String localFileUrl = null;
	String fileName = null;
	try {
		Md5Util md5 = Md5Util.getInstance();
		fileName = md5.string2string(fileUrl)+ "." +suffix;
			
		URL url = new URL(fileUrl);
		//設定代理
		Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("內網網路地址", 埠號));  
	        //開啟連結  
	        HttpURLConnection conn = (HttpURLConnection)url.openConnection(proxy);  
	        //新增header驗證資訊
	        conn.setRequestProperty("Proxy-Authorization", "使用者名稱:密碼");  
	        conn.setRequestProperty("Host", "網址");  
	        conn.setRequestProperty("Cookie", "XXXXXXXXX");  
	        //設定請求方式為"GET"  
	        conn.setRequestMethod("GET");
	        //超時響應時間為5秒  
	        conn.setConnectTimeout(5 * 1000);  
	        //通過輸入流獲取資料  
	        InputStream inStream = conn.getInputStream();
	        //得到二進位制資料,以二進位制封裝得到資料,具有通用性  
	        byte[] data = readInputStream(inStream);
	        //new一個檔案物件用來儲存,預設儲存當前工程根目錄
	        HttpServletRequest request = ServletActionContext.getRequest();
	        //獲取伺服器快取目錄
	        String directoryURL = request.getSession().getServletContext().getRealPath("/");   //D:\workspace\.metadata\.me_tcat7\webapps\EducationalSystem\
//	        String directoryURL = "/home/........";  //線上環境linuxs此處我是用的是絕對路徑
	        //建立本地檔案儲存目錄
	        File fileMkdir = new File(directoryURL + "/cacheFile/"+supportSchoolId());
	        if(!fileMkdir.exists()){
	        	fileMkdir.mkdirs();
	        }
	        
	        localFileUrl = fileMkdir + "/" + fileName;	//檔案路徑
	        File localFile = new File(localFileUrl);
	        if(!localFile.exists()){
	        	//建立檔案
	        	localFile.createNewFile();
	        }
	        //建立輸出流  
	        FileOutputStream outStream = new FileOutputStream(localFile);  
	        //寫入資料  
	        outStream.write(data);
	        //關閉輸出流  
	        outStream.close();
	} catch (Exception e) {
		e.printStackTrace();
	}
	return fileName;
}
	
public byte[] readInputStream(InputStream inStream) throws Exception{  
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();  
        //建立一個Buffer字串  
        byte[] buffer = new byte[1024];  
        //每次讀取的字串長度,如果為-1,代表全部讀取完畢  
        int len = 0;  
        //使用一個輸入流從buffer裡把資料讀取出來  
        while( (len=inStream.read(buffer)) != -1 ){  
            //用輸出流往buffer裡寫入資料,中間引數代表從哪個位置開始讀,len代表讀取的長度  
            outStream.write(buffer, 0, len);  
        }  
        //關閉輸入流  
        inStream.close();  
        //把outStream裡的資料寫入記憶體  
        return outStream.toByteArray();  
}
通過以上程式碼,就實現了將A中資料下載儲存到B中。以上程式碼也是我從網上找了好久才找到的,加上我自己實際中的需求比如新增代理,設定header等。

最後還要說一下我在這個過程中遇到的坑

1、下載到本地伺服器,使用者點選連結時,從本地伺服器獲取連結下載,通過a標籤href屬性實現,但是會跳轉一個空白頁面,不會直接下載,當直接重新整理空白頁面或者將空白頁面地址複製到新的標籤頁訪問時就可以將該檔案下載下來了???解決:通過新增tagart屬性值為_black,在新的標籤頁開啟連線即可。2、該功能我在我自己的筆記本上使用eclipse執行除錯無誤後,部署到linux伺服器上,卻不能下載檔案,錯誤提示404。幾經周折,才明白,是應為A給的代理賬號沒有建立資料夾和檔案的許可權。所以檔案建立失敗。當然也就不可能找到咯,就有了404。解決:xshell等遠端工具,遠端到linux伺服器上,進入到指定目錄,將該目錄許可權修改為777(sudo chmod 777 指定目錄),最後將檔案報錯到改目錄下即可。