文件上传和下载是web开发中常遇到的问题,这几天在做一个项目又用到了文件下载,之前也零零散散记了些笔记,今天来做一下整理。文件上传还有待进一步测试,这里先说一下文件下载。
一、文件下载处理流程
文件下载处理流程其实很清晰,即:
1、根据文件名或者文件路径定位文件,具体的策略主要根据自己的需求,总之需要系统能找到的文件全路径。
2、获取输入流,从目标文件获取输入流。
3、获取输出流,从response中获取输出流。
4、从输入流读入文件,通过输出流输出文件。这是真正的下载执行过程。
5、关闭IO流。
主要流程就是这个,另外就是一些必要的属性设置,比如比较重要的有设置文件的contentType类型等。
二、不啰嗦了了,上代码
我是用Springmvc做的,但其实用其他的也一样,主要需要HttpServletResponse对象和有效的目标文件。
1、前台代码
1
2
3
4
5
6
7
8
9
10
11
12
|
/*
* 下载上传的文件
*/
function downloadFromUpload(fileName){
window.location.href = path +
"/download?dir=upload&fileName="
+encodeURI(encodeURI(fileName));
}
/*
* 普通下载
*/
function download(fileName){
window.location.href = path +
"/download?dir=download&fileName="
+encodeURI(encodeURI(fileName));
}
|
2、controller代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
/**
* 文件下载(从上传路径下载)
*
* @param request
* @param response
* @throws IOException
*/
@ResponseBody
@RequestMapping
(value =
"/download"
)
public
void
downloadFile(HttpServletRequest request,
HttpServletResponse response, FileModel model)
throws
Exception {
String fileName = URLDecoder.decode(model.getFileName(),
"UTF-8"
);
/*
* 限制只有upload和download文件夹里的文件可以下载
*/
String folderName =
"download"
;
if
(!StringUtils.isEmpty(model.getDir())
&& model.getDir().equals(
"upload"
)) {
folderName =
"upload"
;
}
else
{
folderName =
"download"
;
}
String fileAbsolutePath = request.getSession().getServletContext()
.getRealPath(
"/"
)
+
"/WEB-INF/"
+ folderName +
"/"
+ fileName;
FileTools.downloadFile(request, response, fileAbsolutePath);
log.warn(
"用户Id:"
+ (Integer) (request.getSession().getAttribute(
"userId"
))
+
",用户名:"
+ (String) (request.getSession().getAttribute(
"username"
))
+
",下载了文件:"
+ fileAbsolutePath);
}
|
3、下载逻辑实现。这里没有用service了,直接用的静态方法实现。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
/**
* 下载文件时指定下载名
*
* @param request
* HttpServletRequest
* @param response
* HttpServletResponse
* @param filePath
* 文件全路径
* @param fileName
* 指定客户端下载时显示的文件名
* @throws IOException
*/
public
static
void
downloadFile(HttpServletRequest request,
HttpServletResponse response, String filePath, String fileName)
throws
IOException {
BufferedInputStream bis =
null
;
BufferedOutputStream bos =
null
;
bis =
new
BufferedInputStream(
new
FileInputStream(filePath));
bos =
new
BufferedOutputStream(response.getOutputStream());
long
fileLength =
new
File(filePath).length();
response.setCharacterEncoding(
"UTF-8"
);
response.setContentType(
"multipart/form-data"
);
/*
* 解决各浏览器的中文乱码问题
*/
String userAgent = request.getHeader(
"User-Agent"
);
byte
[] bytes = userAgent.contains(
"MSIE"
) ? fileName.getBytes()
: fileName.getBytes(
"UTF-8"
);
// fileName.getBytes("UTF-8")处理safari的乱码问题
fileName =
new
String(bytes,
"ISO-8859-1"
);
// 各浏览器基本都支持ISO编码
response.setHeader(
"Content-disposition"
,
String.format(
"attachment; filename=\"%s\""
, fileName));
response.setHeader(
"Content-Length"
, String.valueOf(fileLength));
byte
[] buff =
new
byte
[
2048
];
int
bytesRead;
while
(-
1
!= (bytesRead = bis.read(buff,
0
, buff.length))) {
bos.write(buff,
0
, bytesRead);
}
bis.close();
bos.close();
}
/**
* 下载文件时不指定下载文件名称
*
* @param request
* HttpServletRequest
* @param response
* HttpServletResponse
* @param filePath
* 文件全路径
* @throws IOException
*/
public
static
void
downloadFile(HttpServletRequest request,
HttpServletResponse response, String filePath)
throws
IOException {
File file =
new
File(filePath);
downloadFile(request, response, filePath, file.getName());
}
|
这里提供了重载的下载方法,解决有时需要指定客户端下载的文件名的需求。
三、注意事项
1、关于MIME类型的选择
之前对MIME类型不是很了解,发现网上有很多下载的源码的MIME类型设置的不一样。即这句
1
|
response.setContentType(
"multipart/form-data"
);
|
2、指定客户端下载文件名
有时我们可能需要指定客户端下载文件时的文件名,即这句代码
1
|
response.setHeader(
"Content-disposition"
, String.format(
"attachment; filename=\"%s\""
, fileName));
|
3、中文乱码问题的解决
中文文件乱码太常见了,在项目系统架构刚搭建时,就应该统一所有的中文编码,包括编辑器中、页面中以及数据库中,推荐UTF-8编码。如果用的Spring,还可以配置Spring的字符集过滤器,进一步避免中文乱码。
(1)客户端下载请求过程文件名乱码
有时我们会遇到,前台页面显示中文文件名下载列表时正常的,但我们到后台发现请求中的文件名乱码了,这时采用前面所说的encodeURI可以解决。
(2)客户端下载执行时文件名乱码
在实际测试中发现,在其他浏览器都可以执行的情况下,ie下中文文件名可能会出现乱码。在网上看到了这样一段代码,经测试,完美解决了不同浏览器的中文乱码问题
1
2
3
4
5
6
7
8
9
|
/*
* 解决各浏览器的中文乱码问题
*/
String userAgent = request.getHeader(
"User-Agent"
);
byte
[] bytes = userAgent.contains(
"MSIE"
) ? fileName.getBytes()
: fileName.getBytes(
"UTF-8"
);
// fileName.getBytes("UTF-8")处理safari的乱码问题
fileName =
new
String(bytes,
"ISO-8859-1"
);
// 各浏览器基本都支持ISO编码
response.setHeader(
"Content-disposition"
,
String.format(
"attachment; filename=\"%s\""
, fileName));
|