夜雨聆风学习资料网

ARTICLE · 1207315

Java 使用 Servlet 上传下载文件

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><htmllang="en"><head>    <metacharset="UTF-8">    <title>上传文件测试</title></head><body><formid="Form1"action="/api/upload"method="post"enctype="multipart/form-data">    <table>        <caption>表单提交,文件上传,支持中文文件名称</caption>        <tr>            <td>请输入内容:</td>            <td><inputtype="text"name="submitName"></td>        </tr>        <tr>            <td>请选择文件01:</td>            <td><inputtype="file"name="upFile1"></td>        </tr>        <tr>            <td>请选择文件02:</td>            <td><inputtype="file"name="upFile2"></td>        </tr>        <tr>            <td></td>            <td>                <buttontype="submit">提交</button>                <buttontype="reset">重置</button>            </td>        </tr>    </table></form><br/><formid="Form2"action="/api/download"method="post">    <table>        <caption>文件下载,支持中文文件名称</caption>        <tr>            <td>请输入下载后的文件命名(不需要输入后缀名):</td>            <td><inputtype="text"name="downLoadName"></td>        </tr>        <tr>            <td>点击按钮下载中文名称的文件:</td>            <td>                <buttontype="submit">下载测试文件</button>            </td>        </tr>    </table></form></body></html>

该页面的界面如下所示:

对于上传文件的表单,文本框中可以录入中文,可以同时上传 2 个文件,上传所选择的文件也可以是中文名称的文件。服务器接收到的表单字段内容,以及文件名称,可以正常接收到中文,不会出现乱码。对于下载文件的表单,可以在文本框中录入你想看到的下载文件名称,可以是中文,不会出现乱码问题。

相关学习资料