先导入jar包
commons-fileupload是Apache开发的一款专门用来处理上传的工具,它的作用就是可以从request对象中解析出,用户发送的请求参数和上传文件的流。
commons-fileupload包依赖commons-io,两个包需要同时导入。
tomcat内置的jar包只能在tomcat服务器使用。
上传文件的方法
1、创建DiskFileItemFactory工厂类的实例
DiskFileItemFactory dfif=new DiskFileItemFactory();//new 一个DiskFileItemFactory对象
DiskFileItemFactory类介绍
工厂类,用于创建ServletFileUpload,设置缓存等
该类一般直接使用构造器直接创建实例
方法:
public void setSizeThreshold(int sizeThreshold)
用于设置缓存文件的大小(默认值10kb)
public void setRepository(File repository)
用于设置缓存文件位置(默认系统缓存目录)
2、获取ServletFileUpload解析器的实例
ServletFileUpload sfu=new ServletFileUpload(dfif);//通过DiskFileItemFactory对象获取ServletFileUpload解析器实例
ServletFileUpload类介绍
该类用于解析request对象从而获取用户发送的请求参数(包括普通参数和文件参数)
该类需要调用有参构造器创建实例,构造器中需要一个DiskFileItemFactory作为参数
方法:
public List<FileItem> parseRequest(HttpServletRequest request)
解析request对象,获取请求参数,返回的是一个List,List中保存的是一个FileItem对象,一个对象代表一个请求参数。
public void setFileSizeMax(long fileSizeMax)
设置单个文件的大小限制,单位为B 如果上传文件超出限制,会在parseRequest()抛出异常FileSizeLimitExceededException。
public void setSizeMax(long sizeMax)
限制请求内容的总大小,单位为B 如果上传文件超出限制,会在parseRequest()抛出异常SizeLimitExceededException。
3、获取请求参数列表
List<FileItem> list = sfu.parseRequest(request);//用ServletFileUpload解析器对象获取参数列表,该列表包含文件参数和普通参数
FileItem类介绍
该类用于封装用户发送的参数和文件,也就是用户发送来的信息将会被封装成一个FileItem对象,我们通过该对象获取请求参数或上传文件的信息。
该类不用我们手动创建,由ServletFileItem解析request后返回。
方法:
String getFieldName()
获取表单项的名字,也就是input当中的name属性的值。
String getName();
获取上传的文件名,普通的请求参数为null。
String getString(String encoding);
获取内容l 若为文件,将文件的流转换为字符串。l 若为请求参数,则获取请求参数的value。
encoding参数需要指定一个字符集,不指定时中文会乱码,建议设置utf-8
boolean isFormField();
判断当前的FileItem封装的是普通请求参数,还是一个文件。如果为普通参数返回:true l 如果为文件参数返回:false
String getContentType();
获取上传文件的MIME类型
long getSize();
获取内容的大小
上传文件的例子
jsp的代码
<form action="${pageContext.request.contextPath }/Files" method="post" enctype="multipart/form-data"> 文件1:<input type="file" name="file1"><br> <br>文件2: <input type="file" name="file2"><br> <br> 文件3:<input type="file" name="file3"><br> <br> 文本:<input type="text" name="text"><br> <br> <input type="submit" value="上传"> </form>
servlet的代码
DiskFileItemFactory dfif = new DiskFileItemFactory();// 创建工厂类 ServletFileUpload sfu = new ServletFileUpload(dfif);// 创建请求解析器 sfu.setFileSizeMax(1024 * 1024 * 1);// 设置上传单个文件的的大小 sfu.setSizeMax(1024 * 1024 * 1 * 10);// 设置上传总文件的大小 response.setContentType("text/html;charset=utf-8");// 设置响应内容的编码 try { List<FileItem> list = sfu.parseRequest(request);// 解析请求信息,获取FileItem的集合 for (FileItem fileItem : list) {// 遍历集合 if (fileItem.isFormField()) {// 如果是普通的表单项 String fieldName = fileItem.getFieldName();// 获取参数名 String value = fileItem.getString("utf-8"); // 获取参数值,设置编码为utf-8防止中文乱码 System.out.println(fieldName + " = " + value); } else {// 如果是文件表单项 String fileName = fileItem.getName();// 获取文件名 String realPath = getServletContext().getRealPath("/upload");// 获取上传路径 System.out.println(realPath); File f = new File(realPath);// 检查upload文件夹是否存在 if (!f.exists()) {//如果不存在则创建 f.mkdir(); } String prefix = UUID.randomUUID().toString().replace("-", "");// 为避免重名生成一个UUID作为文件名的前缀,并且将生成UUID中的-移除 fileItem.write(new File(realPath + "/" + prefix + "_" + fileName));// 将文件写入到服务器中 fileItem.delete();// 清除文件缓存 } } } catch (Exception e) { if (e instanceof SizeLimitExceededException) { response.getWriter().print("上传文件的总大小不能超过10M");// 文件总大小超出限制 } else if (e instanceof FileSizeLimitExceededException) { response.getWriter().print("上传单个文件的大小不能超过1M");// 单个文件大小超出限制 } } response.getWriter().print("上传成功");
下载文件的方法
文件下载最直接的方法就是把文件直接放到服务器的目录中,(eclipse下可以用tc工具快速访问到tomcat的工作目录,具体看我另一片博客)用户直接访问该文件就可以直接下载。但是实际上这种方式并不一定好用,比如我们在服务器上直接放置一个MP3文件,然后通过浏览器访问该文件的地址,如果是IE浏览器可能就会弹出下载窗口,而如果是FireFox和Chrome则有可能直接播放。再有就是有一些文件我们是不希望用户可以直接访问到的,这是我们就要通过Servlet来完成下载功能。
下载文件的几个关键点
1、 服务器以一个流的形式将文件发送给浏览器。
2、发送流的同时还需要设置几个响应头,来告诉浏览器下载的信息。
具体响应头如下:
Content-Type:下载文件的MIME类型,
可以通过servletContext. getMimeType(String file)获取,
也可以直接手动指定,
使用response.setContentType(String type);
响应头样式:Content-Type: audio/mpeg
Content-Disposition:下载文件的名字,
主要作用是提供一个默认的用户名,
通过response.setHeader("Content-Disposition", disposition)设置
响应头样式:Content-Disposition: attachment; filename=xxx.mp3
Content-Length: 下载文件的长度,用于设置文件的长处(不必须)
通过response. setContentLength(int len)设置。
设置后样式:Content-Length: 3140995
3、 接下来需要以输入流的形式读入硬盘上的文件
FileInputStream is = new FileInputStream(file);
这个流就是我们一会要发送给浏览器的内容
4、通过response获取一个输出流,并将文件(输入流)通过该流发送给浏览器
获取输出流 ServletOutputStream out = response.getOutputStream();
通过输出流向浏览器发送文件(不要忘了关闭输入流)
byte[] b = new byte[1024];
int len = 0;
while((len=is.read(b))> 0){
out.write(b, 0, len);
}
is.close();
下载的例子
servlet代码
String realPath = getServletContext().getRealPath("/WEB-INF/download/风吹麦浪.mp3");// 获取文件真实地址 File file = new File(realPath);// 获取file对象 if (!file.exists()) {// 找不到目标文件 response.getWriter().print("文件不存在"); return; } FileInputStream fis = new FileInputStream(file);// 获取文件输入流 String contentType = getServletContext().getMimeType(realPath);// 获取文件的MIME信息 String filename = file.getName();// 设置下载文件的名字 //设置编码格式,否则文件名为中文时会乱码 String ua = request.getHeader("User-Agent");// 获取客户端信息 if (ua.contains("Firefox")) {// 判断客户端是否为火狐 filename = "=?utf-8?B?" + new BASE64Encoder().encode(filename.getBytes("utf-8")) + "?=";// 若为火狐使用BASE64编码 } else { filename = URLEncoder.encode(filename, "utf-8");// 否则使用UTF-8 } String disposition = "attachment; filename=" + filename;// 创建Content-Disposition信息 long size = file.length();// 获取文件长度 response.setContentType(contentType);// 设置Content-Type response.setHeader("Content-Disposition", disposition);// 设置Content-Disposition response.setContentLength((int) size);// 设置文件长度 ServletOutputStream out = response.getOutputStream();// 通过response获取输出流,用于向浏览器输出内容 byte[] buffer = new byte[1024];// 将文件输入流通过输出流输出 int len = 0; while ((len = fis.read(buffer)) > 0) { out.write(buffer, 0, len); } fis.close();// 最后不要忘记关闭输入流,输出流由Tomcat自己处理,不用手动关闭
编码问题
从服务器获取浏览器发送的非文件内容包含中文时,需要设置编码,如String value = fileItem.getString("utf-8"); // 获取参数值,设置编码为utf-8防止中文乱码,不设置则会乱码;
从服务器向浏览器发送中文时,需要对内容进行URL编码。(其他编码问题请看另一篇博客,里边会记录我遇到过的编码问题)
大部分浏览器使用如下方式即可解决乱码问题:URLEncoder.encode(fileName, "utf-8");但是火狐默认以Base64来解码的,所以要为火狐单独处理。
可以使用如下代码来判断浏览器的类型,然后进行不同的编码处理
就是上面例子中使用的方法
//设置编码格式,否则文件名为中文时会乱码 String ua = request.getHeader("User-Agent");// 获取客户端信息 if (ua.contains("Firefox")) {// 判断客户端是否为火狐 filename = "=?utf-8?B?" + new BASE64Encoder().encode(filename.getBytes("utf-8")) + "?=";// 若为火狐使用BASE64编码 } else { filename = URLEncoder.encode(filename, "utf-8");// 否则使用UTF-8 }
还有一种简单但还不理解的方式,亲测可用
向将字符串用gbk进行解码,然后在使用iso8859-1进行编码
filename = new String(filename.getBytes("gbk"),"iso8859-1");
SpringMVC上传文件
Spring MVC 上下文中默认没有为文件上传提供直接的支持,因此默认情况下不能处理文件的上传工作,如果想使用 Spring 的文件上传功能,需现在上下文中配置 CommonsMultipartResovler:
1、与上面一样同样需要导入jar包
2、在SpringMVC配置文件中配置CommonsMultipartResovler
<!-- 配置CommonsMultipartResolver --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="defaultEncoding" value="utf-8"></property> <!-- 以字节为单位 --> <property name="maxUploadSize" value="1024000"></property> </bean>
3、表单:POST请求,file类型,enctype="multipart/form-data"
表单:
<form action="${pageContext.request.contextPath }/fileUpload" method="post" enctype="multipart/form-data"> text:<input type="text" name="text"><br> <br> 文件:<input type="file" name="file"><br> <br> <input type="submit" value="上传"> </form>
controller:
@RequestMapping(value = "/fileUpload") public String fileUpload(HttpServletRequest request, @RequestParam(value = "text", required = false) String text, @RequestParam("file") CommonsMultipartFile file) throws Exception { String realPath = request.getServletContext().getRealPath("/upload");//获取真实路径 //System.out.println(realPath); File file1 = new File(realPath);//创建文件夹 if (!file1.exists()) { file1.mkdir(); } OutputStream out = null; InputStream in = null;//文件输入输出流 String prefix = UUID.randomUUID().toString();//生成唯一UUID String fileName = prefix + "_" + file.getOriginalFilename(); System.out.println(fileName); out = new FileOutputStream(new File(realPath + "\\" + fileName)); in = file.getInputStream(); // IOUtils.copy(in, out);//导入的jar自带的工具类,复制文件,下面是自己写的复制方法 int readIndex = 0; int copyBytes = 0; byte[] buffer = new byte[1024]; while ((readIndex = in.read(buffer)) > 0) { out.write(buffer, 0, readIndex); copyBytes += readIndex; } System.out.println("共复制了:" + copyBytes + "字节"); if (out != null) out.close(); if (in != null) in.close();//关闭流 return "success"; }
SpringMVC文件下载
@RequestMapping(value = "/goFileDownload")//转到下载页面,将文件名显示在页面上 public ModelAndView goFileDownload(HttpServletRequest request) throws Exception { ModelAndView mv = new ModelAndView(); String path = request.getServletContext().getRealPath("/upload/"); System.out.println(path); File file = new File(path);//获取目录 File[] files = file.listFiles(); List<String> fileName = new ArrayList<>(); for (int i = 0; i < files.length; i++)//获取文件名list if (files[i].isFile()) fileName.add(files[i].getName()); mv.setViewName("download"); mv.addObject("files", fileName); return mv; }
@RequestMapping(value = "/fileDownload/{fileName:.+}")//接受文件名参数并下载对应文件,Spring下只需将返回值设置为ResponseEntity<byte[]>即可实现 public ResponseEntity<byte[]> fileDownload(HttpServletRequest request, @PathVariable(value = "fileName") String fileName) throws Exception { //System.out.println(fileName); String path = request.getServletContext().getRealPath("/upload/" + fileName);//获取文件的真实路径 //System.out.println(path); File file = new File(path);//创建文件对象,准备获取内容 InputStream in = new FileInputStream(file); byte[] body = new byte[in.available()]; in.read(body); if (in != null) in.close(); HttpHeaders headers = new HttpHeaders();//设置响应头 fileName = new String(fileName.getBytes("gbk"), "iso8859-1");//防止中文文件名乱码,但是我去掉也能正常下载,可能是我的文件名是接收的原因 headers.add("Content-Disposition", "attachment;filename=" + fileName); HttpStatus statusCode = HttpStatus.OK;//http状态码,此时为200 参考一篇博客 //System.out.println(statusCode); ResponseEntity<byte[]> response = new ResponseEntity<byte[]>(body, headers, statusCode);//设置返回值 return response; }