针对SpringMVC的文件上传和下载。下载用之前“文件上传和下载——基础(一)”的依然可以,但是上传功能要修改,这是因为springMVC 都为我们封装好成自己的文件对象了,转换的过程就在我们所配置的CommonsMultipartResolver里面
原因分析
首先我们来看下Spring mvc 中文件上传的配置
1 <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
2 <!-- one of the properties available; the maximum file size in bytes -->
3 <!-- 5M -->
4 <property name="defaultEncoding" value="utf-8"/>
5 <property name="maxUploadSize" value="25474565"/>
6 </bean>
再来看看Controller中使用
方法一
public void upload2(HttpServletRequest request) {
// 转型为MultipartHttpRequest
try {
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
List<MultipartFile> fileList = multipartRequest.getFiles("file");
for (MultipartFile mf : fileList) {
if(!mf.isEmpty()){
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
方法二
public String upload(HttpServletRequest request, @RequestParam(value = "file") MultipartFile[] files) {
try {
for (MultipartFile mf : files) {
if(!mf.isEmpty()){
}
}
} catch (Exception e) {
e.printStackTrace();
}
return "upload";
}
这里springMVC 都为我们封装好成自己的文件对象了,转换的过程就在我们所配置的CommonsMultipartResolver这个转换器里面下面再来看看它的源码
他的转换器里面就是调用common-fileupload的方式解析,然后再使用parseFileItems()方法封装成自己的文件对象 .
1 List<FileItem> fileItems = ((ServletFileUpload) fileUpload).parseRequest(request);
大家应该发现了上面的这句代码,已经使用过fileUpload解析过request了,你在Controller里面接收到的request都已经是解析过的,你再次使用upload进行解析获取到的肯定是空,这个就是问题的所在(大家可以在servlet里面实验,看看第二次解析后能不能获取到数据,当然是不能的)
解决方案
1)删除Spring MVC文件上传配置
1 <!--
2 <bean id="multipartResolver"
3 class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
4 <property name="defaultEncoding" value="UTF-8" />
5
6 <property name="maxUploadSize" value="2000000000" />
7 </bean>
8 -->
在控制器里面自己完成request的解析(当然上面spring MVC提供的两种方法是不能用的,所有上传的地方都需要自己做处理)
1 public void upload3(HttpServletRequest request) {
2 DiskFileItemFactory factory = new DiskFileItemFactory();
3 ServletFileUpload upload = new ServletFileUpload(factory);
4 try {
5 List<FileItem> list = upload.parseRequest(request);
6 for(FileItem item : list){
7 if(item.isFormField()){
8
9 }else{
10 //item.write(new File(""));
11 }
12 }
13 } catch (FileUploadException e) {
14 e.printStackTrace();
15 }
16
17 }
2)如果是需要使用的ProgressListener监听器我们可以重写 CommonsMultipartResolver的parseRequest方法
1 package com.lwp.spring.ext;
2
3 import java.util.List;
4 import javax.servlet.http.HttpServletRequest;
5 import org.apache.commons.fileupload.FileItem;
6 import org.apache.commons.fileupload.FileUpload;
7 import org.apache.commons.fileupload.FileUploadBase;
8 import org.apache.commons.fileupload.FileUploadException;
9 import org.apache.commons.fileupload.servlet.ServletFileUpload;
10 import org.springframework.web.multipart.MaxUploadSizeExceededException;
11 import org.springframework.web.multipart.MultipartException;
12 import org.springframework.web.multipart.commons.CommonsMultipartResolver;
13 import com.lwp.listener.FileUploadListener;
14 public class CommonsMultipartResolverExt extends CommonsMultipartResolver {
15 @Override
16 protected MultipartParsingResult parseRequest(HttpServletRequest request)
17 throws MultipartException {
18 FileUploadListener listener = new FileUploadListener();
19 String encoding = determineEncoding(request);
20 FileUpload fileUpload = prepareFileUpload(encoding);
21 fileUpload.setProgressListener(listener);
22 try {
23 List<FileItem> fileItems = ((ServletFileUpload) fileUpload).parseRequest(request);
24 return parseFileItems(fileItems, encoding);
25 }
26 catch (FileUploadBase.SizeLimitExceededException ex) {
27 throw new MaxUploadSizeExceededException(fileUpload.getSizeMax(), ex);
28 }
29 catch (FileUploadException ex) {
30 throw new MultipartException("Could not parse multipart servlet request", ex);
31 }
32 }
33 }
监听器方法
1 import org.apache.commons.fileupload.ProgressListener;
2
3 public class FileUploadListener implements ProgressListener {
4
5 @Override
6 public void update(long arg0, long arg1, int arg2) {
7 //arg0 已经上传多少字节
8 //arg1 一共多少字节
9 //arg2 正在上传第几个文件
10 System.out.println(arg0 +"\t" + arg1 +"\t" + arg2);
11 }
12
13 }
配置文件改为我们自己的(这种方式的缺陷是,所有文件上传都需要使用到Listener)
1 <bean id="multipartResolver"
2 class="com.lwp.spring.ext.CommonsMultipartResolverExt">
3 <property name="defaultEncoding" value="UTF-8" />
4 <property name="maxUploadSize" value="2000000000" />
5 </bean>
注: 综上所述,如果只是普通的文件上传spring MVC 完全可以完成,如果需要使用进度条的listener前段可以使用假的进度条或者是上面的两种方式.
看完前辈写过的东西后,我也自己看了一下源代码,有所感想于是进行尝试,结果可行
1 package com.tommy.business;
2
3 import org.apache.commons.fileupload.FileItem;
4 import org.apache.commons.fileupload.FileUploadException;
5 import org.apache.commons.fileupload.disk.DiskFileItemFactory;
6 import org.apache.commons.fileupload.servlet.ServletFileUpload;
7 import org.springframework.stereotype.Controller;
8 import org.springframework.web.bind.annotation.RequestMapping;
9 import org.springframework.web.multipart.MultipartFile;
10 import org.springframework.web.multipart.MultipartHttpServletRequest;
11 import org.springframework.web.multipart.commons.CommonsMultipartFile;
12 import org.springframework.web.multipart.commons.CommonsMultipartResolver;
13
14 import javax.servlet.ServletException;
15 import javax.servlet.http.HttpServletRequest;
16 import javax.servlet.http.HttpServletResponse;
17 import java.io.*;
18 import java.net.URLEncoder;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22
23 /**
24 * Created by OnlyOne on 2016/3/4.
25 */
26 @Controller
27 @RequestMapping("/background/uploadAndDownload/")
28 public class UploadAndDownloadController {
29 /**
30 *
31 * @param req
32 * @param resp
33 * @throws ServletException
34 * @throws IOException
35 * @throws FileUploadException
36 */
37 @RequestMapping("uploadFile")
38 public String uploadFile(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException, FileUploadException {
39 /*//防止中文乱码,与页面字符集一致
40 req.setCharacterEncoding("UTF-8");*/
41 //得到上传文件的保存目录,将上传的文件存放于WEB-INF目录下,不允许外界直接访问,保证上传文件的安全
42 String savePath = req.getServletContext().getRealPath("/WEB-INF/upload");
43 // this.getServletContext().getRealPath("/WEB-INF/upload");
44 //创建保存目录的文件
45 File saveFile = new File(savePath);
46 //判断保存目录文件是否存在,不存在则创建一个文件夹
47 if(!saveFile.exists()){
48 System.out.println("文件目录创建中。。。");
49 saveFile.mkdir();
50 }
51 //消息提示
52 String message = "";
53 //将req转换成Spring的request
54 MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest) req;
55 //获取上传文件
56 List<MultipartFile> list = multipartHttpServletRequest.getFiles("file");
57 //获取普通输入项的数据
58 String map = multipartHttpServletRequest.getParameter("username");
59 System.out.println(map);
60 for(MultipartFile multipartFile: list){
61 FileItem item = ((CommonsMultipartFile)multipartFile).getFileItem();
62 //如果FileItem中封装的是普通输入项的数据
63 if(item.isFormField()){
64 String name = item.getFieldName();
65 //解决普通输入项的数据的中文乱码问题
66 String value = item.getString("UTF-8");
67 // value = new String(value.getBytes("iso8859-1"),"UTF-8");
68 System.out.println(name + "=" + value);
69 }else{//如果fileitem中封装的是上传文件
70 //得到上传的文件名称
71 String fileName = item.getName();
72 System.out.println("文件名是:"+ fileName);
73 if(fileName == null || fileName.trim().equals("")){
74 continue;
75 }
76 /*注意:不同的浏览器提交的文件名是不一样的,有些浏览器提交上来的文件名是带有路径的,
77 如: c:\a\b\1.txt,而有些只是单纯的文件名,如:1.txt*/
78 //处理获取到的上传文件的文件名的路径部分,只保留文件名部分.如果传上来的文件名没有带路径,则lastIndexOf返回-1
79 fileName = fileName.substring(fileName.lastIndexOf("\\")+1);
80 //获取item中的上传输入流
81 BufferedInputStream bis = new BufferedInputStream(item.getInputStream());
82 //创建一个文件输出流
83 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(savePath + "\\" + fileName));
84 //创建一个缓冲区
85 byte[] buffer = new byte[1024*8];
86 //循环将缓冲输入流读入到缓冲区当中
87 while(true){
88 //循环将缓冲输入流读入到缓冲区当中
89 int length = bis.read(buffer);
90 //判断是否读取到文件末尾
91 if(length == -1){
92 break;
93 }
94 //使用BufferedOutputStream缓冲输出流将缓冲区的数据写入到指定的目录(savePath + "\\" + filename)当中
95 bos.write(buffer,0,length);
96 }
97 //关闭输入流
98 bis.close();
99 //关闭输出流
100 bos.close();
101 message = "文件上传成功!";
102 }
103 }
104 return "redirect:listFile.html";
105 }
106
107 /**
108 * @ClassName: ListFileServlet
109 * @Description: 列出Web系统中所有下载文件
110 * @param req
111 * @param resp
112 */
113 @RequestMapping("listFile")
114 public String listFile(HttpServletRequest req, HttpServletResponse resp) {
115 String flag = req.getParameter("flag");
116 //获取上传文件的目录
117 String uploadFilePath = req.getServletContext().getRealPath("/WEB-INF/upload");
118 //存储要下载的文件名
119 Map<String, String> fileNameMap = new HashMap<String, String>();
120 //地鬼遍历filePath目录下的所有文件和目录,将文件的文件名存储到map集合中
121 getListfile(new File(uploadFilePath), fileNameMap);
122 if(flag != null){
123 req.setAttribute("flag",flag);
124 }
125 req.setAttribute("fileNameMap", fileNameMap);
126 return "/background/uploadAndDownload/resourcesList";
127 }
128
129 /**
130 *
131 * @param file 即代表一个文件,也代表一个文件目录
132 * @param map 存储文件名的Map集合
133 * @Method: listfile
134 * @Description: 递归遍历指定目录下的所有文件
135 */
136 private void getListfile(File file, Map<String, String> map) {
137 //如果file代表的不是一个文件,而是一个目录
138 if (!file.isFile()) {
139 //列出该目录下的所有文件和目录
140 File files[] = file.listFiles();
141 //遍历files[]数组
142 for (File f : files) {
143 //递归
144 getListfile(f, map);
145 }
146 } else {
147 /**
148 * 处理文件名,上传后的文件是以uuid_文件名的形式去重新命名的,去除文件名的uuid_部分
149 file.getName().indexOf("_")检索字符串中第一次出现"_"字符的位置,如果文件名类似于:9349249849-88343-8344_阿_凡_达.avi
150 那么file.getName().substring(file.getName().indexOf("_")+1)处理之后就可以得到阿_凡_达.avi部分
151 */
152 //String realName = file.getName().substring(file.getName().indexOf("_") + 1);
153 //file.getName()得到的是文件的原始名称,这个名称是唯一的,因此可以作为key,realName是处理过后的名称,有可能会重复
154 map.put(file.getName(), file.getName());
155 }
156 }
157
158 /**
159 * 下载文件
160 * @param req
161 * @param resp
162 * @throws ServletException
163 * @throws IOException
164 */
165 @RequestMapping("downLoadFile")
166 public void downLoadFile(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
167 //得到要下载的文件名
168 req.setCharacterEncoding("UTF-8");
169 String fileName = req.getParameter("fileName");
170 // fileName = new String(fileName.getBytes("iso8859-1"),"UTF-8");
171 //上传的文件都是保存在/WEN-INF/upload目录党徽宗
172 String fileSavePath = req.getServletContext().getRealPath("/WEB-INF/upload");
173 //得到要下载的文件
174 File file = new File(fileSavePath + "//" + fileName);
175 //如果文件不存在
176 if(!file.exists()){
177 req.setAttribute("message", "资源已被删除!");
178 req.getRequestDispatcher("/message.jsp").forward(req, resp);
179 }
180 //处理文件名
181 String realName = fileName.substring(fileName.indexOf("_")+1);
182 //设置响应头,控制浏览器下载该文件
183 resp.setHeader("content-disposition", "attachment;filename="+ URLEncoder.encode(realName, "UTF-8"));
184 //读取要下载的文件,保存到文件输入流
185 BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fileSavePath + "\\" + fileName));
186 //创建输出流
187 BufferedOutputStream bos = new BufferedOutputStream(resp.getOutputStream());
188 //创建一个缓冲区
189 byte[] buffer = new byte[1024*8];
190 //循环将缓冲输入流读入到缓冲区当中
191 while(true){
192 //循环将缓冲输入流读入到缓冲区当中
193 int length = bis.read(buffer);
194 //判断是否读取到文件末尾
195 if(length == -1){
196 break;
197 }
198 //使用BufferedOutputStream缓冲输出流将缓冲区的数据写入到指定的目录(savePath + "\\" + filename)当中
199 bos.write(buffer,0,length);
200 }
201 //关闭文件输入流
202 bis.close();
203 //刷新此输入流并强制写出所有缓冲的输出字节数
204 bos.flush();
205 //关闭文件输出流
206 bos.close();
207 }
208
209 }
其实只需要将SpringMVC封装的文件对象转换获得原始的数据对象就好了
采用SpringMVC封装的文件对象进行解析
到了这里,发现了将MultipartFile再转换成FileItem不理想,因为MultipartFile没有普通输入项的数据(如:"上传用户:<input type="text"name="username"/><br/>")
还有对文件名的处理也是多余的。
如果要获取普通输入项的数据,也可以。如下
从这里可以看出,Spring对浏览器提交的文件名已经做了处理,不再需要自己处理上传的文件名。
修改之后的完善版本
1 package com.tommy.business;
2
3 import org.apache.commons.fileupload.FileItem;
4 import org.apache.commons.fileupload.FileUploadException;
5 import org.apache.commons.fileupload.disk.DiskFileItemFactory;
6 import org.apache.commons.fileupload.servlet.ServletFileUpload;
7 import org.springframework.stereotype.Controller;
8 import org.springframework.web.bind.annotation.RequestMapping;
9 import org.springframework.web.multipart.MultipartFile;
10 import org.springframework.web.multipart.MultipartHttpServletRequest;
11 import org.springframework.web.multipart.commons.CommonsMultipartFile;
12 import org.springframework.web.multipart.commons.CommonsMultipartResolver;
13
14 import javax.servlet.ServletException;
15 import javax.servlet.http.HttpServletRequest;
16 import javax.servlet.http.HttpServletResponse;
17 import java.io.*;
18 import java.net.URLEncoder;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22
23 /**
24 * Created by OnlyOne on 2016/3/4.
25 */
26 @Controller
27 @RequestMapping("/background/uploadAndDownload/")
28 public class UploadAndDownloadController {
29 /**
30 *
31 * @param req
32 * @param resp
33 * @throws ServletException
34 * @throws IOException
35 * @throws FileUploadException
36 */
37 @RequestMapping("uploadFile")
38 public String uploadFile(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException, FileUploadException {
39 /*//防止中文乱码,与页面字符集一致
40 req.setCharacterEncoding("UTF-8");*/
41 //得到上传文件的保存目录,将上传的文件存放于WEB-INF目录下,不允许外界直接访问,保证上传文件的安全
42 String savePath = req.getServletContext().getRealPath("/WEB-INF/upload");
43 // this.getServletContext().getRealPath("/WEB-INF/upload");
44 //创建保存目录的文件
45 File saveFile = new File(savePath);
46 //判断保存目录文件是否存在,不存在则创建一个文件夹
47 if(!saveFile.exists()){
48 System.out.println("文件目录创建中。。。");
49 saveFile.mkdir();
50 }
51 //消息提示
52 //将req转换成Spring的request
53 MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest) req;
54 //获取上传文件
55 List<MultipartFile> list = multipartHttpServletRequest.getFiles("file");
56 //获取普通输入项的数据
57 String map = multipartHttpServletRequest.getParameter("username");
58 System.out.println(map);
59 for(MultipartFile multipartFile: list){
60 if(!multipartFile.isEmpty()){
61 //得到上传的文件名称
62 String fileName = multipartFile.getOriginalFilename();
63 System.out.println("文件名是:"+ fileName);
64 if(fileName == null || fileName.trim().equals("")){
65 continue;
66 }
67 //获取item中的上传输入流
68 BufferedInputStream bis = new BufferedInputStream(multipartFile.getInputStream());
69 //创建一个文件输出流
70 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(savePath + "\\" + fileName));
71 //创建一个缓冲区
72 byte[] buffer = new byte[1024*8];
73 //循环将缓冲输入流读入到缓冲区当中
74 while(true){
75 //循环将缓冲输入流读入到缓冲区当中
76 int length = bis.read(buffer);
77 //判断是否读取到文件末尾
78 if(length == -1){
79 break;
80 }
81 //使用BufferedOutputStream缓冲输出流将缓冲区的数据写入到指定的目录(savePath + "\\" + filename)当中
82 bos.write(buffer,0,length);
83 }
84 //关闭输入流
85 bis.close();
86 //关闭输出流
87 bos.close();
88 }
89 }
90 return "redirect:listFile.html";
91 }
92
93 /**
94 * @ClassName: ListFileServlet
95 * @Description: 列出Web系统中所有下载文件
96 * @param req
97 * @param resp
98 */
99 @RequestMapping("listFile")
100 public String listFile(HttpServletRequest req, HttpServletResponse resp) {
101 String flag = req.getParameter("flag");
102 //获取上传文件的目录
103 String uploadFilePath = req.getServletContext().getRealPath("/WEB-INF/upload");
104 //存储要下载的文件名
105 Map<String, String> fileNameMap = new HashMap<String, String>();
106 //地鬼遍历filePath目录下的所有文件和目录,将文件的文件名存储到map集合中
107 getListfile(new File(uploadFilePath), fileNameMap);
108 if(flag != null){
109 req.setAttribute("flag",flag);
110 }
111 req.setAttribute("fileNameMap", fileNameMap);
112 return "/background/uploadAndDownload/resourcesList";
113 }
114
115 /**
116 *
117 * @param file 即代表一个文件,也代表一个文件目录
118 * @param map 存储文件名的Map集合
119 * @Method: listfile
120 * @Description: 递归遍历指定目录下的所有文件
121 */
122 private void getListfile(File file, Map<String, String> map) {
123 //如果file代表的不是一个文件,而是一个目录
124 if (!file.isFile()) {
125 //列出该目录下的所有文件和目录
126 File files[] = file.listFiles();
127 //遍历files[]数组
128 for (File f : files) {
129 //递归
130 getListfile(f, map);
131 }
132 } else {
133 /**
134 * 处理文件名,上传后的文件是以uuid_文件名的形式去重新命名的,去除文件名的uuid_部分
135 file.getName().indexOf("_")检索字符串中第一次出现"_"字符的位置,如果文件名类似于:9349249849-88343-8344_阿_凡_达.avi
136 那么file.getName().substring(file.getName().indexOf("_")+1)处理之后就可以得到阿_凡_达.avi部分
137 */
138 //String realName = file.getName().substring(file.getName().indexOf("_") + 1);
139 //file.getName()得到的是文件的原始名称,这个名称是唯一的,因此可以作为key,realName是处理过后的名称,有可能会重复
140 map.put(file.getName(), file.getName());
141 }
142 }
143
144 /**
145 * 下载文件
146 * @param req
147 * @param resp
148 * @throws ServletException
149 * @throws IOException
150 */
151 @RequestMapping("downLoadFile")
152 public void downLoadFile(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
153 //得到要下载的文件名
154 req.setCharacterEncoding("UTF-8");
155 String fileName = req.getParameter("fileName");
156 // fileName = new String(fileName.getBytes("iso8859-1"),"UTF-8");
157 //上传的文件都是保存在/WEN-INF/upload目录党徽宗
158 String fileSavePath = req.getServletContext().getRealPath("/WEB-INF/upload");
159 //得到要下载的文件
160 File file = new File(fileSavePath + "//" + fileName);
161 //如果文件不存在
162 if(!file.exists()){
163 req.setAttribute("message", "资源已被删除!");
164 req.getRequestDispatcher("/message.jsp").forward(req, resp);
165 }
166 //处理文件名
167 String realName = fileName.substring(fileName.indexOf("_")+1);
168 //设置响应头,控制浏览器下载该文件
169 resp.setHeader("content-disposition", "attachment;filename="+ URLEncoder.encode(realName, "UTF-8"));
170 //读取要下载的文件,保存到文件输入流
171 BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fileSavePath + "\\" + fileName));
172 //创建输出流
173 BufferedOutputStream bos = new BufferedOutputStream(resp.getOutputStream());
174 //创建一个缓冲区
175 byte[] buffer = new byte[1024*8];
176 //循环将缓冲输入流读入到缓冲区当中
177 while(true){
178 //循环将缓冲输入流读入到缓冲区当中
179 int length = bis.read(buffer);
180 //判断是否读取到文件末尾
181 if(length == -1){
182 break;
183 }
184 //使用BufferedOutputStream缓冲输出流将缓冲区的数据写入到指定的目录(savePath + "\\" + filename)当中
185 bos.write(buffer,0,length);
186 }
187 //关闭文件输入流
188 bis.close();
189 //刷新此输入流并强制写出所有缓冲的输出字节数
190 bos.flush();
191 //关闭文件输出流
192 bos.close();
193 }
194
195 }
What a meaningless sense if losing myself,though owning all of the world.