携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第19天,点击查看活动详情
文件下载的方式
多种文件下载方式
1. 从本地路径返回文件资源返回给前端(一次性读取)
将文件以流的形式一次性读取到内存,通过响应输出流输出到前端
/**
* @param path 想要下载的文件的路径
* @param response
* @功能描述 下载文件:
*/
@RequestMapping("/download")
public void download(String path, HttpServletResponse response) {
try {
// path是指想要下载的文件的路径
File file = new File(path);
log.info(file.getPath());
// 获取文件名
String filename = file.getName();
// 获取文件后缀名
String ext = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
log.info("文件后缀名:" + ext);
// 将文件写入输入流
FileInputStream fileInputStream = new FileInputStream(file);
InputStream fis = new BufferedInputStream(fileInputStream);
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
fis.close();
// 清空response
response.reset();
// 设置response的Header
response.setCharacterEncoding("UTF-8");
// Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存
// attachment表示以附件方式下载 inline表示在线打开 "Content-Disposition: inline; filename=文件名.mp3"
// filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
// 告知浏览器文件的大小
response.addHeader("Content-Length", "" + file.length());
OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");
outputStream.write(buffer);
outputStream.flush();
} catch (IOException ex) {
ex.printStackTrace();
}
}
2.从本地路径返回文件资源返回给前端(循环读取)
将输入流中的数据循环写入到响应输出流中,而不是一次性读取到内存,通过响应输出流输出到前端
/**
* @param path 指想要下载的文件的路径
* @param response
* @功能描述 下载文件:将输入流中的数据循环写入到响应输出流中,而不是一次性读取到内存
*/
@RequestMapping("/downloadLocal")
public void downloadLocal(String path, HttpServletResponse response) throws IOException {
// 读到流中
InputStream inputStream = new FileInputStream(path);// 文件的存放路径
response.reset();
response.setContentType("application/octet-stream");
String filename = new File(path).getName();
response.addHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(filename, "UTF-8"));
ServletOutputStream outputStream = response.getOutputStream();
byte[] b = new byte[1024];
int len;
//从输入流中读取一定数量的字节,并将其存储在缓冲区字节数组中,读到末尾返回-1
while ((len = inputStream.read(b)) > 0) {
outputStream.write(b, 0, len);
}
inputStream.close();
}
3. 通过网络路径获取文件资源下载到本地
有个问题,就是无法确定资源文件的文件类型,但是可以通过URLConnection类的getContextType获取文件的类型(这是浏览器识别的文件格式类型,不同浏览器对于同一类的文件格式会有不同的表示),但是对应到具体的,如txt,jpg,png,doc等,如何判断,需要后期考究一下
/**
* @param path 下载后的文件路径和名称
* @param netAddress 文件所在网络地址
* @功能描述 网络文件下载到服务器本地
*/
@RequestMapping("/netDownloadLocal")
public void downloadNet(String path, String netAddress) throws IOException {
// 文件资源的网络地址
String netAddress = "https://gimg2.baidu.com/image_search/src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fq_70%2Cc_zoom%2Cw_640%2Fimages%2F20180822%2Fd75840b4fc284e638c3fddb5571117b6.jpeg&refer=http%3A%2F%2F5b0988e595225.cdn.sohucs.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1663484177&t=5913bdab94188cd4686a963737a8a6c8";
// 需要下载到的本地地址
String path = "D:\apps\attachment\common\20220820\123";
String fileName = "test.jpg";
URL url = new URL(netAddress);
URLConnection conn = url.openConnection();
InputStream inputStream = conn.getInputStream();
File localFile = new File(path, fileName);
// 文件夹判空
if (!localFile.exists()){
if (!localFile.getParentFile().exists()){
localFile.getParentFile().mkdirs();
}
localFile.createNewFile();
}
FileOutputStream fileOutputStream = new FileOutputStream(path + File.separator + fileName);
int bytesum = 0;
int byteread;
byte[] buffer = new byte[1024];
while ((byteread = inputStream.read(buffer)) != -1) {
// 记录文件的大小size
bytesum += byteread;
System.out.println(bytesum);
fileOutputStream.write(buffer, 0, byteread);
}
fileOutputStream.close();
}
4. 网络文件获取到服务器后,经服务器处理后响应给前端
网络文件获取到服务器后,经服务器处理后响应给前端
4. 网络文件获取到服务器后,经服务器处理后响应给前端
/**
* @param netAddress
* @param filename
* @param isOnLine
* @param response
* @功能描述 网络文件获取到服务器后,经服务器处理后响应给前端
*/
@RequestMapping("/netDownLoadNet")
public void netDownLoadNet(String netAddress, String filename, boolean isOnLine, HttpServletResponse response) throws Exception {
URL url = new URL(netAddress);
URLConnection conn = url.openConnection();
InputStream inputStream = conn.getInputStream();
response.reset();
response.setContentType(conn.getContentType());
if (isOnLine) {
// 在线打开方式 文件名应该编码成UTF-8
response.setHeader("Content-Disposition", "inline; filename=" + URLEncoder.encode(filename, "UTF-8"));
} else {
//纯下载方式 文件名应该编码成UTF-8
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(filename, "UTF-8"));
}
byte[] buffer = new byte[1024];
int len;
OutputStream outputStream = response.getOutputStream();
while ((len = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, len);
}
inputStream.close();
}
实战
创建Controller层,这里没有做前端演示,做个后端演示吧,文件路径后台写死的,但是都是差不多这么个意思。
实际上在企业中针对系统中上传的附件,会建立相应的表做维护,会对文件的名字、文件格式类型、文件后缀、文件字节大小、文件父路径、文件存储路径
可以参考下面你的sql创建语句:
CREATE TABLE `t_file` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键id',
`business_id` int NOT NULL DEFAULT '0' COMMENT '业务id',
`new_file_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '新文件名',
`extension` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '文件后缀',
`resource_url` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '文件存储路径',
`size` varchar(200) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '文件大小',
`type` varchar(120) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '文件类型',
`upload_time` datetime NOT NULL COMMENT '上传时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=197 DEFAULT CHARSET=utf8mb3 ROW_FORMAT=DYNAMIC;
在文件上传完成后,可以对该附件表进行一个插入操作,进行附件信息的保存和管理
方式一演示
访问连接
http://localhost:8080/download
@GetMapping("/download")
public void download(@RequestParam String path, HttpServletResponse response) {
try {
log.info("path:{}", path);
path = "D:\apps\attachment\common\20220815\8247419878397460480.jpg";
// path是指想要下载的文件的路径
File file = new File(path);
log.info(file.getPath());
// 获取文件名
String filename = file.getName();
log.info("文件父路径:{}", file.getParentFile());
log.info("文件名:{}", filename);
// 获取文件后缀名
String ext = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
log.info("文件后缀名:" + ext);
// 将文件写入输入流
FileInputStream fileInputStream = new FileInputStream(file);
InputStream fis = new BufferedInputStream(fileInputStream);
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
fis.close();
// 清空response
response.reset();
// 设置response的Header
response.setCharacterEncoding("UTF-8");
// Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存
// attachment表示以附件方式下载 inline表示在线打开 "Content-Disposition: inline; filename=文件名.mp3"
// filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
// 告知浏览器文件的大小
response.addHeader("Content-Length", "" + file.length());
log.info("文件大小:{}", file.length());
OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");
outputStream.write(buffer);
outputStream.flush();
} catch (IOException ex) {
ex.printStackTrace();
}
}
结果
2022-08-21 23:23:33.864 INFO 7500 --- [nio-8080-exec-3] c.y.t.controller.DownLoadController : path:
2022-08-21 23:23:33.864 INFO 7500 --- [nio-8080-exec-3] c.y.t.controller.DownLoadController : D:\apps\attachment\common\20220815\8247419878397460480.jpg
2022-08-21 23:23:33.864 INFO 7500 --- [nio-8080-exec-3] c.y.t.controller.DownLoadController : 文件父路径:D:\apps\attachment\common\20220815
2022-08-21 23:23:33.864 INFO 7500 --- [nio-8080-exec-3] c.y.t.controller.DownLoadController : 文件名:8247419878397460480.jpg
2022-08-21 23:23:33.864 INFO 7500 --- [nio-8080-exec-3] c.y.t.controller.DownLoadController : 文件后缀名:jpg
2022-08-21 23:23:33.865 INFO 7500 --- [nio-8080-exec-3] c.y.t.controller.DownLoadController : 文件大小:221757
方式二演示
访问连接
http://localhost:8080/downloadLocal?path=
@GetMapping("/downloadLocal")
public void downloadLocal(String path, HttpServletResponse response) throws IOException {
path = "D:\apps\attachment\common\20220815\8247419878397460480.jpg";
// 读到流中
InputStream inputStream = new FileInputStream(path);// 文件的存放路径
response.reset();
response.setContentType("application/octet-stream");
File file = new File(path);
String filename = (String) file.getName();
log.info("父路径:{}", file.getParentFile());
log.info("文件名字:{}", filename);
// 获取文件后缀名
String ext = filename.substring(filename.lastIndexOf(".") + 1).toLowerCase();
log.info("文件后缀名:" + ext);
response.addHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(filename, "UTF-8"));
ServletOutputStream outputStream = response.getOutputStream();
byte[] b = new byte[1024];
int len;
int sumsize = 0;
//从输入流中读取一定数量的字节,并将其存储在缓冲区字节数组中,读到末尾返回-1
while ((len = inputStream.read(b)) > 0) {
outputStream.write(b, 0, len);
sumsize += len;
}
log.info("文件大小:{}", sumsize);
inputStream.close();
}
结果
2022-08-21 23:39:15.827 INFO 1804 --- [nio-8080-exec-1] c.y.t.controller.DownLoadController : 父路径:D:\apps\attachment\common\20220815
2022-08-21 23:39:15.828 INFO 1804 --- [nio-8080-exec-1] c.y.t.controller.DownLoadController : 文件名字:8247419878397460480.jpg
2022-08-21 23:39:15.828 INFO 1804 --- [nio-8080-exec-1] c.y.t.controller.DownLoadController : 文件后缀名:jpg
2022-08-21 23:39:15.835 INFO 1804 --- [nio-8080-exec-1] c.y.t.controller.DownLoadController : 文件大小:221757
方式三演示
访问连接:
http://localhost:8080/netDownloadLoca
@GetMapping("/netDownloadLocal")
public void downloadNet(String path, String netAddress) throws IOException {
// 文件资源的网络地址
netAddress = "https://gimg2.baidu.com/image_search/src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fq_70%2Cc_zoom%2Cw_640%2Fimages%2F20180822%2Fd75840b4fc284e638c3fddb5571117b6.jpeg&refer=http%3A%2F%2F5b0988e595225.cdn.sohucs.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1663484177&t=5913bdab94188cd4686a963737a8a6c8";
// 需要下载到的本地地址
path = "D:\apps\attachment\common\20220820\123";
String fileName = "test.jpg";
URL url = new URL(netAddress);
URLConnection conn = url.openConnection();
InputStream inputStream = conn.getInputStream();
String contentType = conn.getContentType();
log.info("contentType:{}", contentType);
File localFile = new File(path, fileName);
log.info("文件父路径:{}", path);
log.info("文件名:{}", fileName);
// 文件夹判空
if (!localFile.exists()){
if (!localFile.getParentFile().exists()){
localFile.getParentFile().mkdirs();
}
localFile.createNewFile();
}
FileOutputStream fileOutputStream = new FileOutputStream(path + File.separator + fileName);
int bytesum = 0;
int byteread;
byte[] buffer = new byte[1024];
while ((byteread = inputStream.read(buffer)) != -1) {
// 记录文件的大小size
bytesum += byteread;
System.out.println(bytesum);
fileOutputStream.write(buffer, 0, byteread);
}
log.info("文件大小:{}", bytesum);
fileOutputStream.close();
}
结果
2022-08-22 00:00:24.926 INFO 2788 --- [nio-8080-exec-1] c.y.t.controller.DownLoadController : contentType:image/jpeg
2022-08-22 00:00:24.927 INFO 2788 --- [nio-8080-exec-1] c.y.t.controller.DownLoadController : 文件父路径:D:\apps\attachment\common\20220820\123
2022-08-22 00:00:24.927 INFO 2788 --- [nio-8080-exec-1] c.y.t.controller.DownLoadController : 文件名:test.jpg
2022-08-22 00:00:24.932 INFO 2788 --- [nio-8080-exec-1] c.y.t.controller.DownLoadController : 文件大小:23337
可以看到对应的目录文件下已经把刚刚的网络文件资源下载成功了
方式四演示
访问 isOnline是true表示在线预览:
http://localhost:8080/netDownLoadNet?isOnLine=true
@GetMapping("/netDownLoadNet")
public void netDownLoadNet(String netAddress, String filename, boolean isOnLine, HttpServletResponse response) throws Exception {
// 文件资源的网络地址
netAddress = "https://gimg2.baidu.com/image_search/src=http%3A%2F%2F5b0988e595225.cdn.sohucs.com%2Fq_70%2Cc_zoom%2Cw_640%2Fimages%2F20180822%2Fd75840b4fc284e638c3fddb5571117b6.jpeg&refer=http%3A%2F%2F5b0988e595225.cdn.sohucs.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1663484177&t=5913bdab94188cd4686a963737a8a6c8";
filename = "test.jpg";
URL url = new URL(netAddress);
URLConnection conn = url.openConnection();
InputStream inputStream = conn.getInputStream();
String contentType = conn.getContentType();
log.info("contentType:{}", contentType);
response.reset();
response.setContentType(conn.getContentType());
if (isOnLine) {
// 在线打开方式 文件名应该编码成UTF-8
response.setHeader("Content-Disposition", "inline; filename=" + URLEncoder.encode(filename, "UTF-8"));
} else {
//纯下载方式 文件名应该编码成UTF-8
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(filename, "UTF-8"));
}
byte[] buffer = new byte[1024];
int len;
int bytesum = 0;
OutputStream outputStream = response.getOutputStream();
while ((len = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, len);
// 记录文件的大小size
bytesum += len;
}
log.info("文件大小:{}", bytesum);
inputStream.close();
}
结果
2022-08-22 00:09:43.023 INFO 2192 --- [nio-8080-exec-1] c.y.t.controller.DownLoadController : contentType:image/jpeg
2022-08-22 00:09:43.028 INFO 2192 --- [nio-8080-exec-1] c.y.t.controller.DownLoadController : 文件大小:23337
发送请求后,要求浏览器在线预览
资料来源: