是否通过SpringMVC下载文件,其实原理是一样的,无非就是要设置3个响应头:
- Content-Length : 下载的内容长度,以字节计算
- Content-Type : 内容类型,使用MIME-Type(多用途互联网邮件扩展类型)
-
Content-Disposition : 处理方式
其中Content-Disposition响应头在不同的浏览器上面处理还是不同的,所以需要特别处理!有下面两种主要方式:
- attachment; filename=FILE_NAME 其中FILE_NAME是用URLEncoder编码后的文件名,用UTF-8的编码。支持IE的文件名处理。
-
attachment; filename*=UTF-8''FILE_NAME 这种方式可以指定FILE_NAME的字符编码,其中FILE_NAME也是要用URLEncoder进行编码!支持非
IE浏览器。
特别两个问题:
- 使用非IE浏览器,并且通过*=''方式指定字符编码的时候,必须要确保filename后面有个*,并且在字符编码后面必须跟着两个单引号!格式非常重要哦,否则浏览器不认识的。
-
文件下载的时候,URL最好不要有中文名。通常来讲,文件上传到服务器以后,不会直接把文件名给用户的,而是把文件的ID给用户;下载时通过文件的ID来找到实际的文件信息。
ResponseEntity对象,是SpringMVC里面完全的自定义响应体,包括响应码、响应头、响应体都可以自定义。这里就是利用ResponseEntity实现文件的下载,这种方式脱离了Servlet的API,但是也对程序做出了限制:文件不能超过2G、下载大文件时需要较大内存。
- @RequestMapping(value = "/{xx}", method = RequestMethod.GET)
- public ResponseEntity<byte[]> download(//
- @PathVariable("xx") String id, // 文件id
- @RequestHeader("User-Agent") String userAgent// 浏览器版本
- ) throws UnsupportedEncodingException {
- // 根据文件的id获取文件信息,里面包括文件大小、中文文件名、文件的内容类型
- FileInfo info = this.fileService.getById(id);
- if (info == null) {
- // 404
- log.error("无法根据路径找到对应的文件信息");
- return ResponseEntity.notFound().build();
- } else {
- // 构建响应消息
- // ok() 其实就是 HTTP 200 响应
- BodyBuilder builder = ResponseEntity.ok();
- builder.contentLength(info.getFileLength());// 内容长度
- builder.contentType(//
- MediaType.parseMediaType(info.getContentType())// 内容类型
- );
- // 获得实际的文件名,实际的开发中需要根据不同的浏览器来进行判断做不同的编码
- // 实际的浏览器类型可以通过请求头来获取
- String name = info.getName();
- name = URLEncoder.encode(name, "UTF-8");
- // 设置实际的响应文件名,告诉浏览器文件要用于【下载】、【保存】
- // 不同的浏览器,处理方式不同,要根据浏览器版本进行区别判断
- if (userAgent.indexOf("MSIE") > 0) {
- // 如果是IE,只需要用UTF-8字符集进行URL编码即可
- builder.header("Content-Disposition", "attachment; filename=" + name);
- } else {
- // 而Google、FireFox、Chrome等浏览器,则需要说明编码的字符集
- // 注意filename后面有个*号,在UTF-8后面有两个单引号!
- builder.header("Content-Disposition", "attachment; filename*=UTF-8''" + name);
- }
- // 根据实际的文件路径得到文件,并且转换为byte[]
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- try (FileInputStream in = new FileInputStream(info.getPath())) {
- // 把输入流里面的信息,读取出来转换为byte[]
- byte[] buf = new byte[1024];
- for (int count = in.read(buf); count != -1; count = in.read(buf)) {
- out.write(buf, 0, count);
- }
- byte[] data = out.toByteArray();
- // 构建响应体
- ResponseEntity<byte[]> entity = builder.body(data);
- return entity;
- } catch (IOException e) {
- log.error("找到了对应的文件信息,但是读取文件内容失败:" + e.getLocalizedMessage(), e);
- // 文件没有找到,或者读取失败
- return ResponseEntity.notFound().build();
- }
- }
-
}
如果下载的文件大于2G怎么办?如果不想要占用那么大的内存怎么办?SpringMVC已经提出了非常完美的解决方案,那就是:StreamingResponseBody!
-
@RequestMapping("/download")
-
public StreamingResponseBody handle() {
-
return new StreamingResponseBody() {
-
@Override
-
public void writeTo(OutputStream outputStream) throws IOException {
-
// write...
-
}
-
};
- }
第二种方法
-
@RequestMapping("/download")
-
@RequestMapping(value = "/{xx}", method = RequestMethod.GET)
-
public ResponseEntity<StreamingResponseBody> download(//
-
@PathVariable("xx") String id, // 文件id
-
@RequestHeader("User-Agent") String userAgent// 浏览器版本
-
) throws UnsupportedEncodingException {
-
-
BodyBuilder builder = ResponseEntity.ok();
-
builder.contentLength(info.getFileLength());// 内容长度
-
builder.contentType(//
-
MediaType.parseMediaType(info.getContentType())// 内容类型
-
);
-
-
//
-
StreamingResponseBody body = new StreamingResponseBody(){
-
@Override
-
public void writeTo(OutputStream outputStream) throws IOException {
-
// write...
-
}
-
};
-
-
ResponseEntity<byte[]> entity = builder.body(body);
-
return entity;
-
}
转载地址
http://www.crazyit.org/thread-12193-1-1.html - @RequestMapping(value = "/{xx}", method = RequestMethod.GET)
- public ResponseEntity<byte[]> download(//
- @PathVariable("xx") String id, // 文件id
- @RequestHeader("User-Agent") String userAgent// 浏览器版本
- ) throws UnsupportedEncodingException {
- // 根据文件的id获取文件信息,里面包括文件大小、中文文件名、文件的内容类型
- FileInfo info = this.fileService.getById(id);
- if (info == null) {
- // 404
- log.error("无法根据路径找到对应的文件信息");
- return ResponseEntity.notFound().build();
- } else {
- // 构建响应消息
- // ok() 其实就是 HTTP 200 响应
- BodyBuilder builder = ResponseEntity.ok();
- builder.contentLength(info.getFileLength());// 内容长度
- builder.contentType(//
- MediaType.parseMediaType(info.getContentType())// 内容类型
- );
- // 获得实际的文件名,实际的开发中需要根据不同的浏览器来进行判断做不同的编码
- // 实际的浏览器类型可以通过请求头来获取
- String name = info.getName();
- name = URLEncoder.encode(name, "UTF-8");
- // 设置实际的响应文件名,告诉浏览器文件要用于【下载】、【保存】
- // 不同的浏览器,处理方式不同,要根据浏览器版本进行区别判断
- if (userAgent.indexOf("MSIE") > 0) {
- // 如果是IE,只需要用UTF-8字符集进行URL编码即可
- builder.header("Content-Disposition", "attachment; filename=" + name);
- } else {
- // 而Google、FireFox、Chrome等浏览器,则需要说明编码的字符集
- // 注意filename后面有个*号,在UTF-8后面有两个单引号!
- builder.header("Content-Disposition", "attachment; filename*=UTF-8''" + name);
- }
- // 根据实际的文件路径得到文件,并且转换为byte[]
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- try (FileInputStream in = new FileInputStream(info.getPath())) {
- // 把输入流里面的信息,读取出来转换为byte[]
- byte[] buf = new byte[1024];
- for (int count = in.read(buf); count != -1; count = in.read(buf)) {
- out.write(buf, 0, count);
- }
- byte[] data = out.toByteArray();
- // 构建响应体
- ResponseEntity<byte[]> entity = builder.body(data);
- return entity;
- } catch (IOException e) {
- log.error("找到了对应的文件信息,但是读取文件内容失败:" + e.getLocalizedMessage(), e);
- // 文件没有找到,或者读取失败
- return ResponseEntity.notFound().build();
- }
- }
- }
-
@RequestMapping(value = "/{xx}", method = RequestMethod.GET)
-
public ResponseEntity<StreamingResponseBody> download(//
-
@PathVariable("xx") String id, // 文件id
-
@RequestHeader("User-Agent") String userAgent// 浏览器版本
-
) throws UnsupportedEncodingException {
-
-
BodyBuilder builder = ResponseEntity.ok();
-
builder.contentLength(info.getFileLength());// 内容长度
-
builder.contentType(//
-
MediaType.parseMediaType(info.getContentType())// 内容类型
-
);
-
-
//
-
StreamingResponseBody body = new StreamingResponseBody(){
-
@Override
-
public void writeTo(OutputStream outputStream) throws IOException {
-
// write...
-
}
-
};
-
-
ResponseEntity<byte[]> entity = builder.body(body);
-
return entity;
- }