axios實現下載功能,伺服器之間進行檔案傳輸
阿新 • • 發佈:2019-01-09
需求描述:
需求其實很簡單就是檔案下載。
前端以下簡稱為:client
後端伺服器簡稱:oneServer
檔案所在伺服器簡稱:fileServer
解決方案
- 運用jcifs包中的SmbFile方法,oneServer只需要知道fileServer的使用者名稱密碼,就可以直接訪問到fileServer中的檔案。
SmbFile file = new SmbFile(“smb://guest:[email protected]/uplo/test.txt”);
此方法詳細實現可以百度哈。沒采用該方案的原因:
- 因為我們公司的伺服器密碼是時常更新的,每次都到伺服器密碼系統申請才能登入。
- 這種方法會存在一定的安全問題。
- 將fileServer做成FTP伺服器,這樣oneServer訪問fileServer就也很方便。
沒采用該方案的原因:
- 公司已經有檔案伺服器了,只是我們專案特殊,沒有接入而已
- 由於我之前沒有做過FTP伺服器,怕耗時過長,影響專案進度。
- 即使做出了FTP伺服器,後續架構評審也會非常耗時。
- 寫一個JavaWeb專案到fileServer,讓其提供檔案下載介面,oneServer通過請求該介面下載檔案併發送給client。
採用該方案的原因
- 無需公司架構評審。只需要寫一個javaWeb程式提供服務即可。
- 只需要操作流,fileServer讀取檔案資訊流傳送給oneServer,oneServer直接把流傳輸給前端,只需要考慮資訊流的高效即可。
以上這三種方案都是可以實現的,肯定不只侷限於這三種方案。才疏學淺,只能想到這三種,有好的方法還請告知。
實現過程
1.前端需要用axios傳送請求,因為後臺需要做校驗,如果直接用window.location.href
,無法向後端傳遞使用者資訊(sessionId),因為我們的前端用的vue,前後端連線會跨域,所以axios請求裡面需要自己設定sessionId。
前端程式碼如下:
downloadfile(){
if(this.systemFolderName == null){
this.$Message.error({
content:"請選擇系統",
duration:2
});
return;
}
if(this.fileName == null){
this.$Message.error({
content:"請選擇檔案",
duration:2
});
return;
}
var postData = this.$qs.stringify({
path: this.systemFolderName + "/" + this.fileName
});
this.$axios({
method: 'post',
url: 'downloadfile/remotefile',
data: postData,
responseType: 'arraybuffer'
}).then(response => {
if (response.headers['content-type'].indexOf('json') === -1) {// 返回的資料不是
this.download(response.data);
}else{
if (response.request.responseType === 'arraybuffer' && response.data.toString() === '[object ArrayBuffer]') {
// 返回的資料是 arraybuffer,內容是 json
var text = Buffer.from(response.data).toString('utf8');
var json = JSON.parse(text);
if("N" == json.code){
this.$Message.error({
content:json.message,
duration:2
});
}
}
}
}).catch((error) => {
})
},
download (data) {
if (!data) {
return;
}
let url = window.URL.createObjectURL(new Blob([data]))
let link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', this.fileName)
document.body.appendChild(link)
link.click()
}
此方法借鑑和參考以下兩位博主的部落格:
1、http://www.cnblogs.com/yulj/p/8494465.html
2、http://blog.tubumu.com/2017/12/27/axios-extension-01/
**注意:**如果只用部落格一的方法,後端下載如果報錯,那麼前端依舊會顯示成功,需要再前後端做溝通,統一報錯資訊,以便前端判斷。
{"code":"N","body":null,"message":"Required String parameter 'path' is not present","status":null}
這是我們專案統一的返回格式,所以需要如程式碼中所寫,在下載失敗時做提示。
2.oneServer接收client傳輸的引數,向fileServer傳送請求,並接收fileServer返回的資訊流,然後返回給client,其實就是充當中轉站。
oneServer實現程式碼如下:
public void remoteFile(@NotNull(message = "路徑不能為空") @RequestParam("path") String path, HttpServletResponse response,
HttpServletRequest request) throws IOException, ServletException {
String url = REMOTE_URL + "?selectPath=" + path;
HttpClient httpclient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
HttpResponse res = httpclient.execute(httpGet);
byte[] datas=null;
if (res.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
HttpEntity entity = res.getEntity();
datas = EntityUtils.toByteArray(entity);
}
response.setContentType("multipart/form-data");
response.setCharacterEncoding("UTF-8");
try ( ByteArrayInputStream in = new ByteArrayInputStream(datas);
OutputStream out = response.getOutputStream()) {
int b = 0;
byte[] buffer = new byte[1024];
while (b != -1) {
b = in.read(buffer);
out.write(buffer, 0, b);
}
out.flush();
} catch (IOException e) {
logger.error("前端取消下載", e);
}
}
此處應該可以改進,我本身對IO並不熟悉,這些方法這是可以實現需求,我還沒有深入瞭解,如果錯誤還請留言之處,謝謝。
3.fileServer接收oneServer的請求後,去讀取檔案,並將檔案返回給oneServer。
fileServer實現程式碼如下:
public void downloadFile(String selectPath, HttpServletResponse response) throws IOException {
response.setContentType("multipart/form-data");
response.setCharacterEncoding("UTF-8");
File file = new File(LogsFileConfigure.LOGS_ROOT_PATH + selectPath);
try (
OutputStream out = response.getOutputStream();
FileInputStream inputStream = new FileInputStream(file)) {
int b = 0;
byte[] buffer = new byte[1024];
while (b != -1) {//不能一次性讀完,大檔案會記憶體溢位(不能直接fis.read(buffer);
b = inputStream.read(buffer);
out.write(buffer, 0, b);
}
out.flush();
} catch (IOException e) {
logger.error("讀取檔案失敗", e);
}
}