文件上传和下载(可批量上传)——Spring(二)

  针对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前段可以使用假的进度条或者是上面的两种方式.

以上出自:http://blog.csdn.net/lwphk/article/details/43015829#

看完前辈写过的东西后,我也自己看了一下源代码,有所感想于是进行尝试,结果可行

  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 }

 

posted @ 2016-03-05 14:43  路途寻码人  阅读(1460)  评论(0编辑  收藏  举报