JavaWeb学习总结(十三)文件上传与文件下载

JavaWeb学习总结(十三)文件上传与文件下载

文件的上传和下载,是非常常见的功能。很多的系统中,或者软件中都经常使用文件的上传和下载。 比如:QQ 头像,就使用了上传;邮箱中也有附件的上传和下载功能;OA 系统中审批有附件材料的上传

一、文件上传

(一)步骤

  1. 要有一个 form 标签,method=post 请求
  2. form 标签的 enctype 属性值必须为 multipart/form-data
  3. 在 form 标签中使用 input type=file 添加上传的文件
  4. 编写服务器代码(Servlet 程序)接收,处理上传的数据。

enctype=multipart/form-data 表示提交的数据,以多段(每一个表单项一个数据段)的形式进行拼接,然后以二进制流的形式发送给服务器。

(二)文件上传中HTTP 协议的说明

upload.jsp:
JavaWeb学习总结(十三)文件上传与文件下载_第1张图片

Servlet:

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class UploadServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("文件上传过来了");
    }
}

web.xml:

<servlet>
    <servlet-name>UploadServletservlet-name>
    <servlet-class>com.fox.servlet.UploadServletservlet-class>
servlet>
<servlet-mapping>
    <servlet-name>UploadServletservlet-name>
    <url-pattern>/uploadServleturl-pattern>
servlet-mapping>

JavaWeb学习总结(十三)文件上传与文件下载_第2张图片
在这里插入图片描述

谷歌浏览器按F12,点击Network查看请求体和请求体:
JavaWeb学习总结(十三)文件上传与文件下载_第3张图片

(三)Servlet解析上传的文件

上面我们只是简单地完成了文件上传的步骤,还没有在Servlet中解析上传的文件
要想完整实现文件上传,我们需要用到commons-fileupload.jar包,而commons-fileupload.jar 需要依赖 commons-io.jar 这个包,所以两个包我们都要引入(可以在Maven仓库搜索下载)。

1.commons-fileupload.jar 常用 API 介绍说明

commons-fileupload.jar 中,我们常用的类和方法有哪些?

  • ServletFileUpload

    类,用于解析上传的数据。

    • public static final boolean isMultipartContent(HttpServletRequest request)判断当前上传的数据格式是否是多段的格式。
    • public List parseRequest(HttpServletRequest request) 解析上传的数据
  • FileItem类,表示每一个表单项。

    • boolean isFormField() 判断当前这个表单项,是普通的表单项(true);还是上传的文件类型(false)
    • String getFieldName() 获取表单项的 name 属性值
    • String getString() 获取当前表单项的value 属性值。
    • String getName() 获取上传的文件名
    • void write( file )将上传的文件写到 参数 file 所指向的硬盘位置

2.文件上传的最终实现

upload.jsp和web.xml还是和上面一样
Servlet:

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.List;

public class UploadServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //先判断上传的数据是否多段数据(只有是多段的数据,才是文件上传的)
        if(ServletFileUpload.isMultipartContent(request)){
            //创建 FileItemFactory 工厂实现类
            FileItemFactory fileItemFactory = new DiskFileItemFactory();
            // 创建用于解析上传数据的工具类 ServletFileUpload 类
            ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
            try {
                // 解析上传的数据,得到每一个表单项 FileItem
                List<FileItem> list = servletFileUpload.parseRequest(request);
                // 循环判断,每一个表单项,是普通类型,还是上传的文件
                for (FileItem fileItem : list) {
                    if(fileItem.isFormField()){
                        // 普通表单项
                        System.out.println("表单项的name属性值:"+fileItem.getFieldName());
                        System.out.println("表单项的value属性值:"+fileItem.getString("UTF-8"));
                    }else{
                        //上传的文件
                        System.out.println("表单项的name属性值:"+fileItem.getFieldName());
                        System.out.println("上传的文件名:"+fileItem.getName());
                        //将上传的文件写入硬盘
                        fileItem.write(new File("d://test/"+fileItem.getName()));
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

JavaWeb学习总结(十三)文件上传与文件下载_第4张图片
在这里插入图片描述
于是,服务器就收到了上传的文件:
JavaWeb学习总结(十三)文件上传与文件下载_第5张图片

二、文件下载

(一)文件下载常用API说明:

  • response.getOutputStream()返回适合在响应中写入二进制数据的Servlet输出流。
  • servletContext.getResourceAsStream(String path)将位于指定路径的资源作为输入流对象返回。
  • servletContext.getMimeType(String file)返回指定文件的MIME类型,如果MIME类型未知,则返回null。什么是MIME类型?
  • response.setContentType(String type)设置发送到客户端的响应的内容的MIME类型
  • response.setHeader("Content-Disposition", "attachment; fileName=文件名")这个响应头告诉浏览器,收到的数据是需要下载的。 attachment 表示附件,也就是下载的一个文件;fileName 表示下载的文件名。

(二)文件下载步骤

JavaWeb学习总结(十三)文件上传与文件下载_第6张图片

首先在web目录下新建一个file目录,并提前准备好一张图片
JavaWeb学习总结(十三)文件上传与文件下载_第7张图片
Servlet:

import org.apache.commons.io.IOUtils;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class DownloadServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1、获取要下载的文件名
        String downloadFileName="pyy.jpg";
        //获取ServletContext对象
        ServletContext servletContext = getServletContext();

        //2、获取要下载的文件的MIME类型
        //斜杠/被服务器解析表示地址为http://ip地址:端口号/工程名/  映射到代码中来是web目录
        String mimeType = servletContext.getMimeType("/file/" + downloadFileName);
        System.out.println("下载的文件类型:"+mimeType);

        //3、回传前,通过设置响应头告诉客户端返回的MIME类型
        response.setContentType(mimeType);

        //4、回传前,通过设置响应头告诉客户端收到的数据是用于下载
        //Content-Disposition响应头:表示收到的数据怎么处理
        //attachment:附件的意思,表示用于下载
        //filename:表示指定下载的文件名
        response.setHeader("Content-Disposition","attachment; filename="+downloadFileName);

        //5、读取要下载的文件内容(通过ServletContext对象可以读取)
        InputStream resourceAsStream = servletContext.getResourceAsStream("/file/" + downloadFileName);
        //获取响应的输出流
        OutputStream outputStream = response.getOutputStream();

        //6、把下载的文件内容回传给客户端
        //copy方法读取输入流中全部的数据,复制给输出流,再输出给客户端
        IOUtils.copy(resourceAsStream,outputStream);
    }
}

web.xml:

<servlet>
    <servlet-name>DownloadServletservlet-name>
    <servlet-class>com.fox.servlet.DownloadServletservlet-class>
servlet>
<servlet-mapping>
    <servlet-name>DownloadServletservlet-name>
    <url-pattern>/downloadServleturl-pattern>
servlet-mapping>

在这里插入图片描述

完成上面的步骤,下载文件是没问题了。但是如果我们要下载的文件的文件名是中文的话,你会发现,下载的文件名不正确,是乱码。
原因是在响应头中,不能包含有中文字符,只能包含 ASCII 码。
那么如何解决中文乱码问题呢?

(三)中文乱码问题解决方案

1.解决 IE 和谷歌浏览器的中文乱码问题

如果客户端浏览器是 IE 浏览器或者是谷歌浏览器,我们需要使用 URLEncoder 类先对中文名进行 UTF-8 的编码操作。这样,IE 浏览器和谷歌浏览器收到含有编码后的字符串后会以 UTF-8 字符集进行解码显示。

解决方案

//把中文名进行 UTF-8 编码操作,然后把编码后的字符串设置到响应头中 
response.setHeader("Content-Disposition","attachment; filename=" + URLEncoder.encode("中文.jpg", "UTF-8"));

2.解决火狐浏览器的中文乱码问题

火狐浏览器用的是BASE64编码

(1)BASE64 编解码演示

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class Base64Test {
    public static void main(String[] args) throws Exception {
        String str="今天天气真好";
        //创建一个Base64编码器
        BASE64Encoder base64Encoder = new BASE64Encoder();
        //执行Base64编码操作
        String encodeStr = base64Encoder.encode(str.getBytes("UTF-8"));
        //显示Base64编码后的结果
        System.out.println(encodeStr);

        //创建Base64解码器
        BASE64Decoder base64Decoder = new BASE64Decoder();
        //执行Base64解码操作
        byte[] bytes = base64Decoder.decodeBuffer(encodeStr);
        //显示Base64解码后的结果
        String decodeStr = new String(bytes, "UTF-8");
        System.out.println(decodeStr);
    }
}

在这里插入图片描述

(2)对中文进行 BASE64 编码解决乱码问题

如果客户端浏览器是火狐浏览器。 那么我们需要对中文名进行 BASE64 的编码操作。
这时候需要把请求头 Content-Disposition: attachment; filename=中文名 改为:Content-Disposition: attachment; filename==?charset?B?xxxxx?=

现在我们对这段内容: =?charset?B?xxxxx?= 进行一下说明:

  • =?表示编码内容的开始
  • charset 表示字符集
  • B表示BASE64编码
  • xxxx 表示文件名BASE64编码后的内容
  • ?=表示编码内容的结束

解决方案

// 使用固定格式进行 BASE64 编码后,设置到响应头中 
response.setHeader("Content-Disposition","attachment; fileName=" + "=?UTF-8?B?" + new BASE64Encoder().encode("中文.jpg".getBytes("UTF-8")) + "?=");
12

3.文件下载的最终实现

import org.apache.commons.io.IOUtils;
import sun.misc.BASE64Encoder;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;

public class DownloadServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //1、获取要下载的文件名
        String downloadFileName="pyy.jpg";
        //获取ServletContex对象
        ServletContext servletContext = getServletContext();

        //2、获取要下载的文件的MIME类型
        //斜杠/被服务器解析表示地址为http://ip地址:端口号/工程名/  映射到代码中来是web目录
        String mimeType = servletContext.getMimeType("/file/" + downloadFileName);
        System.out.println("下载的文件类型:"+mimeType);

        //3、回传前,通过设置响应头告诉客户端返回的MIME类型
        response.setContentType(mimeType);

        //4、回传前,通过设置响应头告诉客户端收到的数据是用于下载
        //Content-Disposition响应头:表示收到的数据怎么处理
        //attachment:附件的意思,表示用于下载
        //filename:表示指定下载的文件名

        //响应头User-Agent包含浏览器的信息(是哪个浏览器)
        String ua = request.getHeader("User-Agent");
        // 判断是否是火狐浏览器
        if (ua.contains("Firefox")) {
            // 使用固定格式进行 BASE64 编码后,设置到响应头中
            response.setHeader("Content-Disposition","attachment; fileName=" + "=?UTF-8?B?" + new BASE64Encoder().encode("中文.jpg".getBytes("UTF-8")) + "?=");
        }else {
            //把中文名进行 UTF-8 编码操作,然后把编码后的字符串设置到响应头中
            response.setHeader("Content-Disposition","attachment; filename=" + URLEncoder.encode("中文.jpg", "UTF-8"));
        }
        //5、读取要下载的文件内容(通过ServletContext对象可以读取)
        InputStream resourceAsStream = servletContext.getResourceAsStream("/file/" + downloadFileName);
        //获取响应的输出流
        OutputStream outputStream = response.getOutputStream();

        //6、把下载的文件内容回传给客户端
        //copy方法读取输入流中全部的数据,复制给输出流,再输出给客户端
        IOUtils.copy(resourceAsStream,outputStream);
    }
}

你可能感兴趣的:(JavaWeb,学习,servlet,java)