目录
一、文件上传
1.文件上传和下载的使用说明 :
web文件上传和下载需要用到三个jar包(fileupload2版本, tomcat10),如下图所示 :
编辑
2.文件上传基本原理 :
客户端——
(1) 使用表单提交数据
(2) 表单属性action仍然指向目标web资源(数据提交给该web资源)
(3) 表单属性method指定为post
(4) 表单属性enctype,指encodetype,编码类型;默认为application/x-www-form-urlencoded,即url编码,这种编码不适合二进制文件数据的提交,一般是适用于文本数据。若想进行二进制文件的提交,需要指定enctype为multipart/form-data,表示表单提交的数据是由多个部分组成,也就是可以提交二进制数据和文本数据。
服务端——
(1) 判断是否为一个文件表单;
(2) 判断文件表单所提交的各个表单项分别是什么类型;
(3) 如果是一个普通文本表单项,就按照文本的方式来处理;如果是一个文件表单项(二进制数据) ,就使用IO技术进行处理。
(4) 把表单提交的文件数据,保存到指定的服务端的某个目录。
3.文件上传经典案例 :
3.1 页面实现:
页面效果如下 :
编辑
upload.jsp代码如下 :
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <base href="<%=request.getContextPath()+"/"%>>"> <style type="text/css"> table, td { border:2px pink dashed; border-collapse: collapse; padding: 5px } img { border-radius: 50%; } input[type="file"] { position: absolute; left: 0; top: 0; height: 200px; opacity: 0; cursor: pointer } input[type="submit"] { width: 150px } #td01 { width: 100px; text-align: center } </style> <script type="text/javascript"> function prev(event) { //获取展示图片的区域 var img = document.getElementById("prevView"); //获取文件对象 var file = event.files[0]; //获取文件阅读器(JS的一个类FileReader) var reader = new FileReader(); reader.readAsDataURL(file); reader.onload = function () { //给img的src设置图片url img.setAttribute("src", this.result); } } </script> </head> <body> <!-- form表单属性的注意事项 : (1) action : 注意此页面中base标签的指定 (2) enctype="multipart/form-data", 表示提交的数据是多个部分构造,有文件和文本 --> <form action="fileUploadServlet" method="post" enctype="multipart/form-data"> <table width="350px"> <tr> <td>上传的图片:</td> <td><img src="2.jpg" alt="" width="200" height="200" id="prevView"> <input type="file" name="pic" id="" value="" onchange="prev(this)"/> </td> </tr> <tr> <td>文件名(self-def):</td> <td><input type="text" name="name"><br/></td> </tr> <tr> <td colspan="2" id="td01"><input type="submit" value="Submit"/></td> </tr> </table> </form> </body> </html>
3.2 servlet实现 :
定义一个servlet来实现文件上传的功能,需要用到导入的jar包的工具类。FileUploadServlet类代码如下 :
package servlet; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.fileupload2.core.DiskFileItem; import org.apache.commons.fileupload2.core.DiskFileItemFactory; import org.apache.commons.fileupload2.jakarta.JakartaServletFileUpload; import utils.WebUtils; import java.io.File; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.UUID; /** * @author : Cyan_RA9 * @version : 21.0 */ @WebServlet(urlPatterns = {"/fileUploadServlet"}) public class FileUploadServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) { //1.判断是否为一个文件表单 if (JakartaServletFileUpload.isMultipartContent(req)) { //2.创建一个DiskFileItemFactory对象(注意创建方式同低版本不同) DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory.Builder().get(); //3.利用得到的diskFileItemFactory对象,创建一个解析上传数据的工具对象 JakartaServletFileUpload<DiskFileItem, DiskFileItemFactory> jakartaServletFileUpload = new JakartaServletFileUpload<>(diskFileItemFactory); jakartaServletFileUpload.setHeaderCharset(StandardCharsets.UTF_8); //乱码问题 //4.利用工具对象,将表单提交的数据(text or file) 封装到DiskFileItem中 try { List<DiskFileItem> diskFileItems = jakartaServletFileUpload.parseRequest(req); //5.遍历获取到的集合 for (DiskFileItem diskFileItem : diskFileItems) { //6.判断是否为文件类型(true表示不是文件,是input type=text) if (diskFileItem.isFormField()) { String name = diskFileItem.getString(StandardCharsets.UTF_8); System.out.println("文件名(self-def) = " + name); } else { //false表示是文件 //7.如果判断为是文件,就将上传的文件保存到本地。 String filePath = "/upload/"; //通过ServletContext对象,得到真实的工作路径 String realPath = req.getServletContext().getRealPath(filePath); System.out.println("realPath = " + realPath); String fileName = diskFileItem.getName(); System.out.println("fileName = " + fileName); //8.由于实际工作的路径与web路径不同,所以需要创建目录 //利用定义的工具类,创建分级目录,提高程序执行的效率。 String finalPath = realPath + WebUtils.getLocalDate(); File file = new File(finalPath); if (!file.exists()) { file.mkdirs(); } //9.将上传的文件拷贝到服务端的指定目录 /* (1) 注意得到Path的两种方式 (2) 解决文件重名问题,给文件名加一个前缀,保证是唯一即可。 */ fileName = UUID.randomUUID().toString() + "_" + fileName; diskFileItem.write(new File(finalPath + fileName).toPath()); } } } catch (IOException e) { throw new RuntimeException(e); } } else { System.out.println("不是一个表单捏~"); } } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } }
3.3 工具类实现 :
定义一个工具类,用来对文件名作标识,以解决文件重名导致的文件覆盖问题。
WebUtils类代码如下 :
package utils; import java.time.LocalDateTime; /** @function : 该方法返回一个日期形式的字符串路径 */ public class WebUtils { public static String getLocalDate() { LocalDateTime ldt = LocalDateTime.now(); int year = ldt.getYear(); int month = ldt.getMonthValue(); //获取月份的值 int dayOfMonth = ldt.getDayOfMonth(); String localDate = year + "/" + month + "/" + dayOfMonth + "/"; return localDate; } }
3.4 运行测试 :
测试结果如下GIF :
编辑
3.5 注意事项 :
(1) 同一个目录下上传了很多文件时,会造成访问文件速度变慢的问题,因此可以将文件上传到不同的目录,例如按照上传日期进行分类, eg : 2023/9/2/。
(2) 一次完美的文件上传需要考虑很多因素,eg : 断点续传,对图片大小和尺寸的限制,分片上传,防止恶意上传等。PS : 在项目中,可以考虑百度开发的WebUploader组件。
(3) 文件上传功能在项目中应该有限制的使用,一般用在头像、证明、合同、产品展示等;如果不加限制,会造成服务器空间被大量占用(eg : 微信朋友圈最多一次发9张)。
(4)Tomcat在启动时,不会为web目录下的空文件夹在out目录下创建对应的文件夹,所以,需要在空文件夹中事先放入一个文件。
二、文件下载
1.文件下载基本原理 :
服务器端要设置两个重要的HTTP响应头——
(1) Content-Disposition : 表示下载数据的展示方式,比如是内联形式(即网页形式或者网页的一部分),或者是文件下载方式(attachment)。
(2) Content-Type : 指定返回数据的MIME类型。
而响应体,在网络传输时是图片的原生数据(即按照浏览器下载的编码),而下载后查看到的是浏览器本身解析后的文件。
2.文件下载经典案例 :
2.1 准备工作
将要下载的文件拷贝到web目录的sources目录下,如下图所示 :
编辑
2.2 页面实现
页面效果如下 :
编辑
download.jsp代码如下 :
<%-- User : Cyan_RA9 Version : 21.0 --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <meta charset="UTF-8"/> <title>Title</title> <base href="<%= request.getContextPath() + "/"%>"/> </head> <body> <h1>文件下载</h1> <a href="fileDownloadServlet?name=miku_flowers.jpg">点我下载初音未来</a> <br/><br/> <a href="fileDownloadServlet?name=3.txt">点我下载王勃的滕王阁诗</a> </body> </html>
2.3 servlet实现
定义一个servlet,完成文件下载的功能。
FileDownloadServlet代码如下 :
package servlet; import jakarta.servlet.ServletException; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.apache.commons.io.IOUtils; import java.io.IOException; import java.io.InputStream; import java.net.URLEncoder; /** * @author : Cyan_RA9 * @version : 21.0 */ @WebServlet(urlPatterns={"/fileDownloadServlet"}) public class FileDownloadServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //1.得到文件名和文件路径(文件资源的路径!) req.setCharacterEncoding("utf-8"); String fileName = req.getParameter("name"); String filePath = "/sources/" + fileName; //2.通过ServletContext对象得到文件的MIME类型 String mimeType = req.getServletContext().getMimeType(filePath); System.out.println("mimeType = " + mimeType); //3.设置第一个响应头Content-Type resp.setContentType(mimeType); //4.设置第一个响应头Content-Disposition //Content-Disposition用于指定下载的数据的展示形式,其中attachment表示使用文件下载方式 //不同浏览器需要的编码格式不同 if (req.getHeader("User-Agent").contains("Chrome")) { resp.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8")); } else { //其他浏览器的编码操作 } //5.读取文件,将读取到的文件返回给客户端(IO流拷贝的封装) //获取与数据源文件相关联的字节输入流 InputStream resourceAsStream = req.getServletContext().getResourceAsStream(filePath); //获取与目的地文件相关联的字节输出流 ServletOutputStream outputStream = resp.getOutputStream(); //利用封装好的工具类,进行IO流拷贝 IOUtils.copy(resourceAsStream, outputStream); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } }
2.4 运行测试
运行效果如下图所示(GIF图)
编辑
2.5 注意事项
(1) 不同的浏览器,对于下载的文件的处理方式不同,有些是直接打开该文件,有些则是将文件下载到本地的指定下载目录。
(2) 对于网站上的文件,很多文件可以使用“另存为”来下载;对于大文件(文档,视频等),往往使用专业的下载工具(eg : 迅雷,百度网盘等)。
(3) 注意,不同的浏览器对文件解码格式的需求不同。