文件下载方案
单文件下载
首先要设置好响应头,提供前端解析
这里由于数据库用的utf-8
编码所以参数给前端需要对中文字段进行编码
响应头设置
//设置响应头
response.setCharacterEncoding("utf-8");
//比特流
response.setContentType("application/octet-stream");
//设置HTTP响应头,以允许浏览器访问服务器返回的特定的HTTP响应头
response.setHeader("Access-Control-Expose-Headers","Content-Disposition");
//设置文件名,设置字符集是避免文件名中有中文时出现乱码
String encodeFileName = null;
try {
//UTF-8编码
encodeFileName = URLEncoder.encode(filePo.getFileName(), StandardCharsets.UTF_8.toString());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
//设置到响应头
response.addHeader("Content-Disposition", "attachment;filename=" + encodeFileName);
将文件输入流写到输出流
这里流的关闭在工具类里处理了
OutputStream outputStream = null;
FileInputStream inputStream = null;
try {
outputStream = response.getOutputStream();
inputStream = new FileInputStream(file);
} catch (IOException e) {
e.printStackTrace();
}
IOUtils.copy(inputStream,outputStream);
IO工具类
将读到buffer里的字节写到输出流,注意读到的字节个数以保证精确读取
public class IOUtils {
public static void copy(InputStream inputStream, OutputStream outputStream) {
try {
byte[] buffer = new byte[1024];
int read;
while (( read = inputStream.read(buffer) ) != -1){
outputStream.write(buffer,0,read);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
inputStream.close();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
文件夹下载
通常浏览器下载文件夹不会返回文件夹,这里我采用了返回ZIP文件将文件夹内容打包返回给前端
构建ZIP文件集合
这里是我项目里面的一些逻辑,其实就是创建两种结点,一种文件夹结点,一种文件结点
如果没有文件夹实体的话,要整合的集合value值用null就行了。
然后文件类型的话value放File对象。
key值代表在ZIP中的路径
文件夹的话一定要以/
结尾 dir1/dir2/
文件的话带上路径/文件名称 结尾即可 dir1/dir2/output.txt
//构建zip内部文件集合
Set<String> keySet = childFileMap.keySet();
for (String path : keySet) {
//创建文件夹结点
zipFileMap.put(path,null);
//获取到文件持久对象列表
List<FilePo> filePoList = childFileMap.get(path);
for (FilePo po : filePoList) {
//获取文件源绝对路径
String location = locations.get(po.getSourceId());
//创建文件对象
File file = new File(location);
//创建文件结点
zipFileMap.put(path+po.getFileName(),file);
}
}
返回给前端
try {
//打包为zip返回前端
ZipFileUtils.zipFilesToOutPutStream(zipFileMap,os);
} catch (IOException e) {
e.printStackTrace();
}
ZIP工具类
package pers.os467.utils;
import java.io.*;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* by os467
*/
public class ZipFileUtils {
/**
* 将输出流包装为zip输出流并按照文件结构映射打包文件
* @param files key为zip中的路径,分隔符必须使用"/" value为文件对象,若为null如果是目录则创建空目录
* 例如
* 新建文件夹1/ -> null
* 新建文件夹1/新建文件夹2 -> null
* 新建文件夹/1.txt -> new File(".../1.txt")
* 新建文件夹1/新建文件夹2/2.txt -> new File(".../2.txt")
* @param out 输出流
* @throws IOException
*/
public static void zipFilesToOutPutStream(Map<String, File> files, OutputStream out) throws IOException {
if (files.size() == 0){
//创建空zip
try (ZipOutputStream zos = new ZipOutputStream(out)){//do nothing...
}
return;
}
// 创建一个ZipOutputStream对象,它将用于写入压缩数据
try (ZipOutputStream zos = new ZipOutputStream(out)) {
// 遍历文件映射
for (Map.Entry<String, File> entry : files.entrySet()) {
// 获取文件的ZIP路径和对应的文件对象
String name = entry.getKey();
File file = entry.getValue();
//如果是目录则创建空目录 例如name = dir1/dir2/
if (file == null) {
zos.putNextEntry(new ZipEntry(name));
zos.closeEntry();
continue;
}
// 如果文件对象不是一个文件,抛出异常
if (!file.isFile()) {
throw new IllegalArgumentException("Only files are supported");
}
// 创建一个新的ZIP条目并将其添加到ZIP输出流中 例如: name = dir1/dir2/fileName
// 这里的name就是文件在压缩包中的路径,包括多级目录
zos.putNextEntry(new ZipEntry(name));
// 创建一个输入流来读取文件的内容
try (InputStream in = new FileInputStream(file)) {
// 创建一个缓冲区
byte[] buffer = new byte[1024];
int len;
// 读取文件内容并写入ZIP输出流
while ((len = in.read(buffer)) != -1) {
zos.write(buffer, 0, len);
}
}
// 关闭当前ZIP条目,使其写入ZIP文件
zos.closeEntry();
}
}
}
}
前端接收文件
最简单的就是 location.href = 后端url
让浏览器自行处理下载即可
但是感觉不够灵活,这边写了个axios的方法
原理就是,利用html5提供给<a>
标签的download属性下载文件,浏览器会识别响应为一个可下载内容。
首先创建blob对象,然后创建一个下载链接
创建一个a标签,并附带href
属性和文件名
这里文件名注意以下,因为后端用的utf-8
编码后传递给前端的 %开头的那种的表示中文的utf8编码,这里先获取下content-dispostion
中filename=后面的部分,就是我们设置的文件名
然后用JS的decodeURIComponent
来解析即可
最后a标签加入到body然后click触发下载即可。
download: function (fid) {
/*location.href=`${BACKEND_URL}/download/file/${fid}`*/
axios.get(`${BACKEND_URL}/download/file/${fid}`,
{responseType: 'blob'}
).then((resp) => {
/*创建blob对象*/
const blob = new Blob([resp.data]);
/*创建下载链接*/
const url = window.URL.createObjectURL(blob);
/*创建链接标签*/
const link = document.createElement('a');
link.href = url;
const fileName = resp.headers['content-disposition'].split('=')[1];
link.setAttribute('download', decodeURIComponent(fileName));
document.body.appendChild(link);
link.click();
/*移除链接标签*/
document.body.removeChild(link);
})
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以邮件至 1300452403@qq.com