Java 使用 Servlet 上传下载文件
在实际开发中,上传文件和下载文件是很常见的功能,如果文件名是中文的话,还容易会出现乱码问题。
本篇博客采用 Servlet 作为接口演示 Java 上传文件和下载文件的实现方案,同时解决获取上传和下载过程中所遇到的的中文文件名乱码问题,并在本篇博客的最下面提供 demo 源代码下载。
一、搭建工程
新建一个 maven 工程,并导入相关的 jar 包和 tomcat 插件,具体内容如下:
有关具体的 jar 包地址,可以在 https://mvnrepository.com 上进行查询。
有关 tomcat 插件,可以到官网查询,地址为:https://tomcat.apache.org/maven-plugin.html
目前 tomcat 官网的插件,最新版本也就是 tomcat7 的 2.2 版本。如果你想使用更高版本的 tomcat 的话,就下载独立的 tomcat 即可。这里只是进行 demo 演示,就直接使用 tomcat 的插件了,能够满足 demo 运行的环境,使用起来也很方便。
<dependencies> <!--servlet支持--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!--文件上传组件支持--> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency> <!--Apache提供的一些实用的工具类支持--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.12.0</version> </dependency> </dependencies> <build> <finalName>fileUpDownTest</finalName> <plugins> <!--Tomcat官网最新版本的tomcat插件--> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <!--tomcat启动的端口号--> <port>80</port> <!--网站启动后访问的虚拟地址--> <path>/</path> </configuration> </plugin> </plugins> </build>
由于要使用 servlet web 开发,需要导入 javax.servlet-api 的 jar 包。另外导入了 Apache 的 commons-fileupload 的 jar 包组件,使文件上传变的很容易,同时导入了 commons-lang3 的 jar 包组件,主要是使用里面的一些字符串处理的实用方法,简化代码开发。最后打开右侧的 Maven 窗口,刷新一下,这样 Maven 会自动下载所需的 jar 包文件。
搭建好的项目工程整体目录比较简单,具体如下图所示:
项目工程结构简单介绍:
com.jobs.controller 包下面放了 2 个 Servlet 处理上传和下载的请求
com.jobs.filter 包下面放了 1 个过滤器,主要设置请求和响应的字符编码,统一解决乱码问题
webapp 下的 upload 用来存放上传的文件,里面提前放了一个中文名称的 txt 文件,用于下载
webapp 下面放了一个 index.html 页面文件,网站启动时进行展示,提供文件上传和下载操作
二、细节展示
这里先说 CharacterEncodingFilter 过滤器,统一设置请求和响应的字符编码为 UTF-8 ,这样可以方式乱码。另外该过滤器配置的作用地址,本 demo 示例只处理 Servlet 请求,不处理其它请求,例如 css、js 等文件请求。具体内容如下:
package com.jobs.filter; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.annotation.WebInitParam; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; //这里只监听 /api/* 的请求,如果你想监听所有请求,可以使用 /* 配置 //另外配置了过滤器的请求和响应的初始化参数,在下面的处理方法中可以获取 @WebFilter(value = "/api/*", initParams = { @WebInitParam(name = "requestEncoding", value = "UTF-8"), @WebInitParam(name = "responseContentType", value = "text/html;charset=UTF-8")}) public class CharacterEncodingFilter implements Filter { private FilterConfig filterConfig; @Override public void init(FilterConfig filterConfig) throws ServletException { this.filterConfig = filterConfig; } @Override public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request; HttpServletResponse response; try { request = (HttpServletRequest) req; response = (HttpServletResponse) resp; //获取初始化参数 String requestEncoding = filterConfig.getInitParameter("requestEncoding"); String responseContentType = filterConfig.getInitParameter("responseContentType"); //使用初始化参数,设置请求和响应的编码 request.setCharacterEncoding(requestEncoding); response.setContentType(responseContentType); chain.doFilter(request, response); } catch (Exception e) { e.printStackTrace(); } } @Override public void destroy() { } }
upLoadServlet 是专门用来处理上传文件的请求,同时也接收 form 表单提交过来的字段,具体内容如下:
package com.jobs.controller; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.lang3.StringUtils; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; import java.util.List; //配置访问地址 @WebServlet("/api/upload") public class upLoadServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse resp) throws IOException { //判断该操作是否支持文件上传操作 //要提交的form表单上必须要有属性 enctype="multipart/form-data" if (ServletFileUpload.isMultipartContent(request)) { try { //将文件保存到 webapp 下的 upload 文件夹中 String dir = this.getServletContext().getRealPath("upload"); File f = new File(dir); if (!f.exists()) { f.mkdir(); } //采用 apache 的上传组件,只需要编写很少的代码,即可完成文件上传功能 DiskFileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload fileUpload = new ServletFileUpload(factory); List<FileItem> fileItems = fileUpload.parseRequest(request); for (FileItem item : fileItems) { //当前表单是否是文件表单 if (item.isFormField()) { //获取表单字段 String fieldName = item.getFieldName(); //必须使用 utf-8 编码获取表单字段的值,否则就是乱码 String fieldValue = item.getString("utf-8"); System.out.println("表单字段:" + fieldName + "," + fieldValue); } else { //此 item 表示文件 //获取文件的表单字段名称 String fieldName = item.getFieldName(); //获取文件的原始文件名,不包含路径 String fileName = item.getName(); System.out.println("上传文件:" + fieldName + "," + fileName); //这里的 StringUtils 是 apache 的 commons-lang3 组件提供的实用工具类 if(StringUtils.isNotEmpty(fileName)) { //将上传的文件,以原始名称保存到具体的目录下, //当然你可以生成随机 uuid 给文件命名 //这里使用原始文件名,主要演示由于设置了过滤器统一处理字符编码, //所以保存中文文件名,不出现乱码的问题 item.write(new File(dir, fileName)); } } } } catch (Exception ex) { ex.printStackTrace(); } } //由于过滤器统一设置了响应编码, //所以响应页面中展示的中文,不会出现乱码 resp.getWriter().write("恭喜你,成功了"); } }
需要注意:由于本 demo 示例采用了 tomcat 插件,因此运行的是源码本身的网站上传文件,文件会保存到 webapp 下的 upload 文件夹下,在 idea 工具中可以立刻看到。如果你使用的是独立的 tomcat 进行部署访问时,上传文件后需要到独立部署的目标 webapp 下的 upload 文件夹下去查看上传的文件。
下面列出 downLoadServlet 的内容,这个是专门用来处理下载文件请求的,具体细节如下:
package com.jobs.controller; import org.apache.commons.lang3.StringUtils; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.*; //配置访问地址 @WebServlet("/api/download") public class downLoadServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException { String parm = req.getParameter("downLoadName"); String downLoadName; if (StringUtils.isNotBlank(parm)) { downLoadName = parm + ".txt"; } else { downLoadName = "测试文件.txt"; } //获取所要下载的文件,这里只是 demo 演示,因此对下载的目标文件进行了硬编码 String dir = this.getServletContext().getRealPath("upload"); File f = new File(dir, "测试文件.txt"); //下载文件的响应类型,这里统一设置成了文件流 //你可以根据自己所提供下载的文件类型,使用不同的响应 mime 类型 response.setContentType("application/octet-stream;charset=utf-8"); //设置下载弹出框中默认显示的文件名称,如果指定中文名称的话,需要转成 iso8859-1 编码,解决乱码问题 String fileName = new String(downLoadName.getBytes(), "iso8859-1"); response.addHeader("Content-Disposition", "attachment;filename=" + fileName); //第一种下载方式,适合文件已经在硬盘上,此时读取文件字节流,直接下载 try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(f))) { ServletOutputStream outputStream = response.getOutputStream(); byte[] bArr = new byte[1024]; int len; while ((len = bis.read(bArr)) != -1) { outputStream.write(bArr, 0, len); } } catch (IOException e) { e.printStackTrace(); } /* //第二种下载方式,适合不在硬盘上生成文件,而是在内存中生成文件字节流,然后下载 //当然这里的例子,还是从硬盘上读取了文件,但是将字节流存入 ByteArrayOutputStream 内存字节流 //模拟使用 ByteArrayOutputStream 生成文件流,然后进行下载 ServletOutputStream outputStream = response.getOutputStream(); try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(f)); ByteArrayOutputStream baos=new ByteArrayOutputStream()){ byte[] bArr = new byte[1024]; int len; while ((len = bis.read(bArr)) != -1) { baos.write(bArr, 0, len); } baos.writeTo(outputStream); } catch(IOException e){ e.printStackTrace(); } outputStream.flush(); */ } }
本 demo 下载的是 webapp 下的 upload 文件夹下的固定文件:测试文件.txt
本 demo 提供了两种下载的方式,第一种是读取硬盘中的文件,采用响应流进行下载。
第二种方式是模拟不生成硬盘文件的前提下,采用内存流的方式生成文件,并最终写入响应流,还是依靠响应流进行下载。
当然本 demo 还是从硬盘中读取了文件,生成该文件的内存流。在实际开发中,我们可能会从数据库中获取记录,然后在内存中生成 Excel 并写入内存流,然后通过响应流进行下载 Excel 文件。
最后展示 index.html 页面的具体内容:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>上传文件测试</title> </head> <body> <form id="Form1" action="/api/upload" method="post" enctype="multipart/form-data"> <table> <caption>表单提交,文件上传,支持中文文件名称</caption> <tr> <td>请输入内容:</td> <td><input type="text" name="submitName"></td> </tr> <tr> <td>请选择文件01:</td> <td><input type="file" name="upFile1"></td> </tr> <tr> <td>请选择文件02:</td> <td><input type="file" name="upFile2"></td> </tr> <tr> <td></td> <td> <button type="submit">提交</button> <button type="reset">重置</button> </td> </tr> </table> </form> <br/> <form id="Form2" action="/api/download" method="post"> <table> <caption>文件下载,支持中文文件名称</caption> <tr> <td>请输入下载后的文件命名(不需要输入后缀名):</td> <td><input type="text" name="downLoadName"></td> </tr> <tr> <td>点击按钮下载中文名称的文件:</td> <td> <button type="submit">下载测试文件</button> </td> </tr> </table> </form> </body> </html>
该页面的界面如下所示:
对于上传文件的表单,文本框中可以录入中文,可以同时上传 2 个文件,上传所选择的文件也可以是中文名称的文件。服务器接收到的表单字段内容,以及文件名称,可以正常接收到中文,不会出现乱码。对于下载文件的表单,可以在文本框中录入你想看到的下载文件名称,可以是中文,不会出现乱码问题。
本 demo 示例的代码下载地址为:https://files.cnblogs.com/files/blogs/699532/fileUpDownTest.zip
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构