一、概述
FileUpload能够以多种不同的方式使用,具体取决于应用程序的要求。在最简单的情况下,调用单个方法来解析servlet请求,然后处理解析出来的Item集合。此外也可以自定义FileUpload已完全控制各个Item的存储方式,比如设置缓存目录、直接将接收到的Item以流的形式写入数据库等。
FileUpload依赖于Commons IO,因此类路径下要有Commons IO的jar包。当然采用Maven依赖的方式不用担心,maven会自动下载Commons IO包。
二、FIleUpload使用
上传文件,注意几个问题:
① form表单内,要添加空间<input type="file" name="myfile">
② form表单的内容格式要定义成multipart/form-data格式:<form action="admin/good_add" method="post" enctype="multipart/form-data">
③ 需要类库:1 commons-io.jar ,2 commons-fileupload-1.3.1.jar
接下来我们看下他的用法。
1)创建工厂类
DiskFileItemFactory factory = new DiskFileItemFactory();
FileItemFactory可以设置内容:
● setRepository(File dir);//设置临时文件存储位置。
● setSizeThreshold(long bytes);//设置请求大小阈值,当请求大于该值时,接收到的数据是缓存在磁盘中的,否则直接缓存到内存中。
● setFileCleaningTracker(FileCleaningTracker pTracker);//设置临时文件清理跟踪器
2)创建核心类-文件解析对象
ServletFileUpload upload = new ServletFileUpload(DiskFileItemFactory factory);
ServletFileUpload可以设置内容:
● setSizeMax(long bytes);//设置整个请求的最大值,大于该值时,不允许传送。
● setFileMaxSize(long bytes);//设置单个文件的最大值,大于该值时,不允许传送。
● setHeaderEncoding(String charset);//设置读取每个FileItem的头数据的字符编码,不设置时采用request编码,也没有时采用系统默认编码。
● serProgressListener(ProgressListener pListener);//设置上传进度监听器
3)解析request请求-进行文件解析后放在List中,因为这个类库支持多个文件上传,因此把结果会存在List中。
List<FileItem> list = upload.parseRequest(request);
FileItem能够获取的内容:
● getContentType();//获取单个Item的ContentType
● getName();//获取Item本来的文件名,如果不是文件则为null
● getFieldName();//获取Item的field名
● getSize();//获取Item的大小
● get();//将Item转换成字节数组返回
● isInMemory();//Item目前是否存在内存中
● isFormField();//是否是表单域
● getInputStream();//获取输入流,用于读取Item
二、上传原理和代码分析
上传:将需要上传的资源,发送给服务器,在服务器中保存下来。
下载:下载资源时,将服务器上的某个资源发送给浏览器。
1)浏览器端
注意:enctype=multipart/form-data:该属性表明发送的请求体的内容是多表单元素的,通俗点讲,就是有各种各样的数据,可能有二进制数据,也可能有表单数据,等等。
1 <form class="form-horizontal" action="${pageContext.request.contextPath }/admin/good_add" method="post" enctype="multipart/form-data"> 2 <div class="form-group"> 3 <label for="input_name" class="col-sm-1 control-label">名称</label> 4 <div class="col-sm-6"> 5 <input type="text" class="form-control" id="input_name" name="name" required="required"> 6 </div> 7 </div> 8 <div class="form-group"> 9 <label for="input_name" class="col-sm-1 control-label">价格</label> 10 <div class="col-sm-6"> 11 <input type="text" class="form-control" id="input_name" name="price"> 12 </div> 13 </div> 14 <div class="form-group"> 15 <label for="input_name" class="col-sm-1 control-label">介绍</label> 16 <div class="col-sm-6"> 17 <input type="text" class="form-control" id="input_name" name="intro"> 18 </div> 19 </div> 20 <div class="form-group"> 21 <label for="input_name" class="col-sm-1 control-label">库存</label> 22 <div class="col-sm-6"> 23 <input type="text" class="form-control" id="input_name" name="stock"> 24 </div> 25 </div> 26 <div class="form-group"> 27 <label for="input_file" class="col-sm-1 control-label">封面图片</label> 28 <div class="col-sm-6"> 29 <input type="file" name="cover" id="input_file" required="required">推荐尺寸: 500 * 500 30 </div> 31 </div> 32 <div class="form-group"> 33 <label for="input_file" class="col-sm-1 control-label">详情图片1</label> 34 <div class="col-sm-6"> 35 <input type="file" name="image1" id="input_file" required="required">推荐尺寸: 500 * 500 36 </div> 37 </div> 38 <div class="form-group"> 39 <label for="input_file" class="col-sm-1 control-label">详情图片2</label> 40 <div class="col-sm-6"> 41 <input type="file" name="image2" id="input_file" required="required">推荐尺寸: 500 * 500 42 </div> 43 </div> 44 <div class="form-group"> 45 <label for="select_topic" class="col-sm-1 control-label">类目</label> 46 <div class="col-sm-6"> 47 <select class="form-control" id="select_topic" name="good.type.id"> 48 <c:forEach items="${typeList }" var="t"> 49 <option value="${t.id }">${t.name }</option> 50 </c:forEach> 51 </select> 52 </div> 53 </div> 54 <div class="form-group"> 55 <div class="col-sm-offset-1 col-sm-10"> 56 <button type="submit" class="btn btn-success">提交保存</button> 57 </div> 58 </div> 59 </form>
使用multipart/form-data会有一个boundary属性,来用将提交的表单数据进行分隔,以用来让服务器知道哪个是我们上传的资源,哪个是普通的表单数据。
1 <form action="adctionServlet" method="post" enctype="enctype=multipart/form-data"> 2 描述:<input type="text" name=""/><br/> 3 图片:<input type="file" name="image"/><br/> 4 <input type="submit" value="上传"/> 5 </form>
2)服务器端
使用commons-fileupload进行处理上传内容。
1 try { 2 DiskFileItemFactory factory = new DiskFileItemFactory(); 3 ServletFileUpload upload = new ServletFileUpload(factory); 4 5 List<FileItem> list = upload.parseRequest(request); 6 Goods goods = new Goods(); 7 for (FileItem item : list) { 8 9 // 判断itme;true-为普通输入项,false-为文件 10 if (item.isFormField()) { 11 12 // 设置商品属性 13 switch (item.getFieldName()) { 14 case "name": 15 goods.setName(item.getString("utf-8")); 16 break; 17 case "price": 18 goods.setPrice(Float.parseFloat(item.getString("utf-8"))); 19 break; 20 case "intro": 21 goods.setIntro(item.getString("utf-8")); 22 break; 23 case "stock": 24 goods.setStock(Integer.parseInt(item.getString("utf-8"))); 25 break; 26 case "good.type.id": 27 goods.setTypeid(Integer.parseInt(item.getString("utf-8"))); 28 break; 29 } 30 } else { 31 32 if (item.getInputStream().available() <= 0) { 33 continue; 34 } 35 // 1-获取文件名;2-获取后缀名;3-根据毫秒值设置新的文件名;4-创建保存路径 36 String fileName = item.getName(); 37 fileName = fileName.substring(fileName.indexOf(".")); 38 fileName = "/" + System.currentTimeMillis() + fileName; 39 String path = this.getServletContext().getRealPath("/picture") + fileName; 40 41 // 读取上传 42 InputStream in = item.getInputStream(); 43 FileOutputStream out = new FileOutputStream(path); 44 byte[] buff = new byte[1024]; 45 int len = 0; 46 while ((len = in.read(buff)) > 0) { 47 out.write(buff, 0, len); 48 } 49 in.close(); 50 out.close(); 51 52 // 设置商品属性 53 switch (item.getFieldName()) { 54 case "cover": 55 goods.setCover("/picture" + fileName); 56 break; 57 case "image1": 58 goods.setImage1("/picture" + fileName); 59 break; 60 case "image2": 61 goods.setImage2("/picture" + fileName); 62 break; 63 } 64 } 65 item.delete(); 66 } 67 } catch (FileUploadException e) { 68 e.printStackTrace(); 69 }
上传的各种问题:
1,上传文件名乱码问题:使用servletFileUpload.setHeaderEncoding("UTF-8");或者request.setCharacterEncoding("UTF-8")都可以。
2,表单内容乱码问题:使用getString("utf-8")即可,也就是在获取内容时,就可以设置码表。
3,上传文件同名问题:使用UUID.randomUUID().toString().replace("-", "").获得一个独一无二的32位数字。
4,使用FileUtils.copyInputStreamToFile(is, file);来将内容输出到指定路径文件中去,mkdirs() 自动创建目录
1 public void doGet(HttpServletRequest request, HttpServletResponse response) 2 throws ServletException, IOException { 3 try { 4 5 //0.5 检查是否支持文件上传 ,检查请求头Content-Type : multipart/form-data 6 if(!ServletFileUpload.isMultipartContent(request)){ 7 throw new RuntimeException("不要得瑟,没用"); 8 } 9 10 //1 工厂 11 DiskFileItemFactory fileItemFactory = new DiskFileItemFactory(); 12 // 1.1 设置是否生产临时文件临界值。大于2M生产临时文件。保证:上传数据完整性。 13 fileItemFactory.setSizeThreshold(1024 * 1024 * 2); //2MB 14 // 1.2 设置临时文件存放位置 15 // * 临时文件扩展名 *.tmp ,临时文件可以任意删除。 16 String tempDir = this.getServletContext().getRealPath("/temp"); 17 fileItemFactory.setRepository(new File(tempDir)); 18 19 //2 核心类 20 ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory); 21 // 2.1 如果使用无参构造 ServletFileUpload() ,手动设置工厂 22 //servletFileUpload.setFileItemFactory(fileItemFactory); 23 // 2.2 单个上传文件大小 24 //servletFileUpload.setFileSizeMax(1024*1024 * 2); //2M 25 // 2.3 整个上传文件总大小 26 //servletFileUpload.setSizeMax(1024*1024*10); //10M 27 // 2.4 设置上传文件名的乱码 28 // * 首先使用 setHeaderEncoding 设置编码 29 // * 如果没有设置将使用请求编码 request.setCharacterEncoding("UTF-8") 30 // * 以上都没有设置,将使用平台默认编码 31 servletFileUpload.setHeaderEncoding("UTF-8"); 32 // 2.5 上传文件进度,提供监听器进行监听。 33 servletFileUpload.setProgressListener(new MyProgressListener()); 34 35 36 //3 解析request ,List存放 FileItem (表单元素的封装对象,一个<input>对应一个对象) 37 List<FileItem> list = servletFileUpload.parseRequest(request); 38 39 //4 遍历集合获得数据 40 for (FileItem fileItem : list) { 41 // 判断 42 if(fileItem.isFormField()){ 43 // 5 是否为表单字段(普通表单元素) 44 //5.1.表单字段名称 45 String fieldName = fileItem.getFieldName(); 46 System.out.println(fieldName); 47 //5.2.表单字段值 , 解决普通表单内容的乱码 48 String fieldValue = fileItem.getString("UTF-8"); 49 System.out.println(fieldValue); 50 } else { 51 //6 上传字段(上传表单元素) 52 //6.1.表单字段名称 fileItem.getFieldName(); 53 //6.2.上传文件名 54 String fileName = fileItem.getName(); 55 // * 兼容浏览器, IE : C:UsersliangtongDesktopabc.txt ; 其他浏览器 : abc.txt 56 fileName = fileName.substring(fileName.lastIndexOf("\") + 1); 57 // * 文件重名 58 fileName = UUID.randomUUID().toString().replace("-", "") + fileName; 59 // * 单个文件夹文件个数过多? 60 String subDir = StringUtils.getDir(fileName); 61 62 System.out.println(fileName); 63 //6.3.上传内容 64 InputStream is = fileItem.getInputStream(); 65 String parentDir = this.getServletContext().getRealPath("/WEB-INF/upload"); 66 File file = new File(parentDir + subDir,fileName); 67 68 // 将指定流 写入 到 指定文件中 -- mkdirs() 自动创建目录 69 FileUtils.copyInputStreamToFile(is, file); 70 71 //7删除临时文件 72 fileItem.delete(); 73 } 74 } 75 76 } catch (Exception e) { 77 e.printStackTrace(); 78 79 throw new RuntimeException(e); 80 81 } 82 }
总结上传:
其实理解了也不是很难,就是上传文件后的处理比较麻烦,各种小问题,存储过程最为麻烦。
1、创建工厂类
2、使用核心类,
3、解析request请求,
4、遍历请求体的内容,将上传内容和普通表单内容都获取出来
5、获取到上传内容时,对其存储位置进行设置
@部分转载自:https://www.cnblogs.com/whgk/p/6479405.html