IDEA 环境配置:导入依赖 jar 包。
commons-fileupload-1.4.jar
commons-io-2.6.jar
1. 文件上传
本质就是将一台电脑中的文件根据网络协议通过 io 流传递到另一台电脑(服务器)上。
1.1 三要素
① 表单数据提交方式:POST
② 表单提交数据的类型:<form ... enctype=multipart/form-data>...</form>
③ 表单中设置文件上传项:<input type="file" ... />
1.2 核心逻辑
前台 JSP 展示页面:
1 2 3 4 5
| <form action="${pageContext.request.contextPath}/upload" method="post" enctype="multipart/form-data"> 描述:<input type="text" name="desc"> <br> 上传附件:<input type="file" name="file"> <br> <button type="submit">提交</button> </form>
|
后台 Servlet 核心逻辑:
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
|
ServletFileUpload servletFileUpload = new ServletFileUpload(new DiskFileItemFactory());
servletFileUpload.setHeaderEncoding("utf-8"); try { List<FileItem> fileItems = servletFileUpload.parseRequest(request); for (FileItem fileItem : fileItems) { if (fileItem.isFormField()) { System.out.println(fileItem.getFieldName()); System.out.println(fileItem.getString("utf-8")); } else { BufferedInputStream bis = new BufferedInputStream(fileItem.getInputStream()); File dirPath = new File(request.getServletContext().getRealPath("upload")); if (!dirPath.exists()) { dirPath.mkdir(); } String path = dirPath + File.separator + System.currentTimeMillis() + "-" + fileItem.getName(); System.out.println(path); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(path)); byte[] bs = new byte[8*1024]; int size; while ((size = bis.read(bs)) != -1) { bos.write(bs, 0, size); } bos.close(); bis.close(); } } } catch (Exception e) { e.printStackTrace(); }
|
文件上传到服务器:
1 2 3 4 5 6 7
| FileService fileService = new FileServiceImpl(); MyFile myFile = new MyFile(); myFile.setFileName(filename); myFile.setFilePath(request.getContextPath() + File.separator + "upload" + File.separator + filename); myFile.setDescription(description); fileService.addFile(myFile);
|
文件上传核心 API:
1 2 3 4 5 6 7 8 9
| ServletFileUpload 核心解析类 parseRequest(HttpServletRequest request) 解析请求,并获取相关文件项 setHeaderEncoding(String encoding) 解决中文文件名乱码 FileItem 文件项 boolean isFormField() 返回为true,普通字段。返回为false,就是文件 String getFieldName() 获取表单 name 字段 String getString(String encoding) 根据指定编码格式获取字段值,解决描述文本中文乱码 String getName() 获取上传文件名称 InputStream getInputStream() 获取上传文件对应的输入流
|
1.3 中文乱码和名字重复
文件名中文乱码:
1 2 3 4 5
|
ServletFileUpload servletFileUpload = new ServletFileUpload(new DiskFileItemFactory());
servletFileUpload.setHeaderEncoding("utf-8");
|
描述内中文乱码:
1 2 3 4 5 6
| if (fileItem.isFormField()) { System.out.println(fileItem.getString("utf-8")); } else { ... }
|
文件名重复问题:
1 2
| String path = serverPath + File.separator + System.currentTimeMillis() + "-" + username + "-" + fileItem.getName();
|
2. 文件下载
本质就是将一台电脑(服务器)中的文件根据网络协议通过 io 流传递到另外一台电脑上。
2.1 三步骤
前端:超链接携带文件名参数
① 设置媒体类型
② 设置下载窗口(内容特征)
③ IO读写(写-响应给浏览器)
2.2 核心逻辑
超链接携带文件名:
1
| <a href="${pageContext.request.contextPath}/download?fileName=${file.fileName}">下载</a>
|
设置媒体类型:
1 2 3 4
| String fileName = request.getParameter("fileName"); String mimeType = request.getServletContext().getMimeType(fileName); response.setContentType(mimeType);
|
**设置下载窗口(内容特征)**:
1 2 3 4
| String userAgent = request.getHeader("User-Agent"); String newFileName = userAgent.contains("Chrome") ? URLEncoder.encode(fileName, "utf-8") : base64EncodeFileName(fileName);
response.setHeader("Content-Disposition", "attachment;filename=" + newFileName);
|
**IO读写(写-响应给浏览器)**:
1 2 3 4 5 6 7 8 9 10 11
| String path = request.getServletContext().getRealPath("upload") + File.separator + fileName; BufferedInputStream bis = new BufferedInputStream(new FileInputStream(path)); BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream()); byte[] bs = new byte[8*1024]; int size = -1; while ((size = bis.read(bs)) != -1) { bos.write(bs, 0, size); } bis.close(); bos.close();
|
2.3 中文乱码处理
不同浏览器编码方式不同:对文件名进行编码时以谷歌为代表的是 utf-8
,其他浏览器是 base64
根据浏览器在下载窗口前设置编码方式:
1 2 3
| String userAgent = request.getHeader("User-Agent"); String newFileName = userAgent.contains("Chrome") ? URLEncoder.encode(fileName, "utf-8") : base64EncodeFileName(fileName); response.setHeader("Content-Disposition", "attachment;filename=" + newFileName);
|
base64编码 API(固定写法) :
1 2 3 4 5 6 7 8 9 10 11
| public String base64EncodeFileName(String fileName) { BASE64Encoder base64Encoder = new BASE64Encoder(); try { return "=?UTF‐8?B?" + new String(base64Encoder.encode(fileName.getBytes("UTF-8"))) + "?="; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } }
|
3. 通用 Servlet 实现文件上传下载
upload.jsp
1 2 3 4 5 6
| <h2>文件上传</h2> <form action="${pageContext.request.contextPath}/file?methodName=upload" method="post" enctype="multipart/form-data"> 描述:<input type="text" name="description" > <br> <br> 上传文件:<input type="file" name="file"> <br> <br> <button type="submit">提交</button> </form>
|
fileList.jsp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <h2>文件列表:</h2> <table border="1px" cellpadding="5px" cellspacing="0px"> <tr> <td>编号</td> <td>文件名</td> <td>文件描述</td> <td>操作</td> </tr>
<c:forEach items="${fileList}" var="file"> <tr> <td>${file.id}</td> <td>${file.fileName}</td> <td>${file.description}</td> <td> <a href="${pageContext.request.contextPath}/file?methodName=download&fileName=${file.fileName}">下载</a> </td> </tr> </c:forEach> </table>
|
FileServlet.java
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 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
| @WebServlet(name = "FileServlet", urlPatterns = "/file") public class FileServlet extends BaseServlet { private FileService fileService = new FileServiceImpl();
public String upload(HttpServletRequest request, HttpServletResponse response) { ServletFileUpload servletFileUpload = new ServletFileUpload(new DiskFileItemFactory()); servletFileUpload.setHeaderEncoding("utf-8"); String filename = null; String description = null; try { List<FileItem> fileItems = servletFileUpload.parseRequest(request); for (FileItem fileItem : fileItems) { if (fileItem.isFormField()) { description = fileItem.getString("utf-8"); } else { File dirPath = new File(request.getServletContext().getRealPath("upload")); if (!dirPath.exists()) { dirPath.mkdir(); } filename = System.currentTimeMillis() + "-" + fileItem.getName(); String path = dirPath + File.separator + filename;
BufferedInputStream bis = new BufferedInputStream(fileItem.getInputStream()); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(path)); byte[] bs = new byte[8192]; int size = 0; while ((size = bis.read(bs)) != -1) { bos.write(bs, 0, size); } bos.close(); bis.close(); } } } catch (Exception e) { e.printStackTrace(); }
FileService fileService = new FileServiceImpl(); FileInfo newFile = new FileInfo(); newFile.setFileName(filename); newFile.setFilePath(request.getContextPath() + File.separator + "upload" + File.separator + filename); newFile.setDescription(description); boolean result = false; try { result = fileService.addFile(newFile); } catch (Exception e) { e.printStackTrace(); } return result ? "r:/file?methodName=fileList" : "/upload.jsp"; }
public String fileList(HttpServletRequest request, HttpServletResponse response) { try { List<FileInfo> fileList = fileService.selectAllFiles(); request.setAttribute("fileList", fileList); } catch (Exception e) { e.printStackTrace(); } return "/fileList.jsp"; }
public void download(HttpServletRequest request, HttpServletResponse response) { String fileName = request.getParameter("fileName"); String mimeType = request.getServletContext().getMimeType(fileName); response.setContentType(mimeType);
String userAgent = request.getHeader("User-Agent"); try { String newFileName = userAgent.contains("Chrome") ? URLEncoder.encode(fileName, "utf-8") : base64EncodeFileName(fileName); response.setHeader("Content-Disposition", "attachment;filename=" + newFileName);
String path = request.getServletContext().getRealPath("upload") + File.separator + fileName; BufferedInputStream bis = new BufferedInputStream(new FileInputStream(path)); BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream()); byte[] bs = new byte[8192]; int size = -1; while ((size = bis.read(bs)) != -1) { bos.write(bs, 0, size); } bos.close(); bis.close(); } catch (Exception e) { e.printStackTrace(); } }
public String base64EncodeFileName(String fileName) { BASE64Encoder base64Encoder = new BASE64Encoder(); try { return "=?UTF‐8?B?" + new String(base64Encoder.encode(fileName.getBytes("UTF-8"))) + "?="; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } }
|
通用 Servlet :
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
| @WebServlet(name = "BaseServlet", urlPatterns = "/base") public class BaseServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String methodName = request.getParameter("methodName"); System.out.println(methodName); try { Method method = this.getClass().getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class); String returnValue = (String) Objects.requireNonNull(method).invoke(this, request, response); if (null != returnValue) { if (returnValue.lastIndexOf(":") != -1) { String path = returnValue.split(":")[1]; if (returnValue.startsWith("r")) { response.sendRedirect(request.getContextPath() + path); } else if (returnValue.startsWith("f")) { request.getRequestDispatcher(path).forward(request, response); } } else { request.getRequestDispatcher(returnValue).forward(request, response); } } } catch (Exception e) { e.printStackTrace(); } }
@Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } }
|