vue的项目,需要做一个文件上传下载的功能。上传是OK的,然鹅因为上传到minio的时候,使用了随机数命名,导致文件名称就变成了一大串字符。被客户驳回来了。那好吧,就加一个重命名功能好了。
不过这个项目好奇怪,正常的后台解析文件流,前端下载的方案,下载下来文件都会变成乱码,怎么改都不行,最后,参考了JavaScript 实现文件下载并重命名 直接在vue端实现了文件的转为blob并下载。 代码如下:
download(downloadUrl,downloadFileName ) {
this.getBlob(downloadUrl).then(blob => {
this.saveAs(blob, downloadFileName + ".pdf");
});
},
getBlob(url) {
return new Promise(resolve => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.onload = () => {
if (xhr.status === 200) {
resolve(xhr.response);
}
};
xhr.send();
});
},
saveAs(blob, filename) {
if (window.navigator.msSaveOrOpenBlob) {
navigator.msSaveBlob(blob, filename);
} else {
const link = document.createElement('a');
const body = document.querySelector('body');
let binaryData = [];
binaryData.push(blob);
link.href = window.URL.createObjectURL(new Blob(binaryData));
link.download = filename;
// fix Firefox
link.style.display = 'none';
body.appendChild(link);
link.click();
body.removeChild(link);
window.URL.revokeObjectURL(link.href);
}
},
直接调用 download(下载地址,重命名的文件名)
即可
==============================================
20210926补充
一个严重的问题,本项目中,这样写,会造成下载的PDF文件损坏,无法打开。因此,最后还是采用了后台解析文件流,传到前端进行下载的方案。具体代码如下:
后台代码:
/**
* 下载附件
* @param response
* @param fileName
*/
@GetMapping(value = "/downloadAttachment")
public ResponseEntity<?> downloadAttachment(final HttpServletResponse response, @RequestParam("fileName") String fileName) {
InputStream inputStream = null;
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
// 获取文件信息
ObjectStat objectStat = template.getObjectInfo(bucketName, fileName);
// 设置响应头
response.setHeader("content-type", objectStat.contentType());
response.setContentType(objectStat.contentType());
// 获取文件输入流
inputStream = template.getObject(bucketName, fileName);
outputStream = this.readInpurStream(inputStream);
ByteArrayResource resource = new ByteArrayResource(outputStream.toByteArray());
return ResponseEntity.ok()
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.header(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment; filename=%s", URLEncoder.encode(fileName+".pdf", "UTF-8")))
.body(resource);
} catch (Exception e) {
log.error("导出附件失败:",e);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("下载文件出现异常");
}
}
/**
* 将input流转化为ByteArrayOutputStream
* @param input
* @return
* @throws Exception
*/
public ByteArrayOutputStream readInpurStream(InputStream input) throws Exception{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
try {
while ((len = input.read(buffer)) > -1) {
baos.write(buffer, 0, len);
}
baos.flush();
} catch (IOException e) {
throw new Exception("Illegal flow.");
} finally {
try {
input.close();
} catch (IOException e) {
log.error("file stream shutdown failed.");
}
}
return baos;
}
前端代码:
download: function () {
let params=Object.assign({
fileName: this.fileAppendix
});
downloadFile(params).then(res => {
exportPdf(res.data, this.downloadFileName);
})
},
export function downloadFile(query) {
return fetchRequest('/downloadAttachment',{
method: 'get',
params: query
})
}
// 导出为pdf
export const exportPdf = (res, name) => {
const blob = new Blob([res]);
let fileName = name ? name + ".pdf" : Date.parse(new Date()) + ".pdf";
if ("download" in document.createElement("a")) {
// 非IE下载
const elink = document.createElement("a");
elink.download = fileName;
elink.style.display = "none";
elink.href = URL.createObjectURL(blob);
document.body.appendChild(elink);
elink.click();
URL.revokeObjectURL(elink.href); // 释放URL 对象
document.body.removeChild(elink);
} else {
// IE10+下载
navigator.msSaveBlob(blob, fileName);
}
};
export default function fetchRequest(url, options) {
let content = {
headers: {
Authorization: `bearer ${getToken()}`,
"Content-type": "application/json",
},
method: options.method,
};
let pathUrl = url;
if (options.method === "get") {
pathUrl = pathUrl + formatUrl(options.params);
} else if (options.method === "post") {
content.body = JSON.stringify(options.data);
}
return fetch(pathUrl, content)
.then(checkStatus)
.then(parseBlob)
.then((data) => ({ data }))
.catch((err) => ({ err }));
}
不过这么写虽然可以解决问题,但是代码就要写的比较多,回头有空再看看有没有更好的写法