文件的上传和下载

发布时间 2023-09-23 22:49:52作者: 一往而深,

文件的上传

  • 1.我们先测试打通服务器
  • upload.jsp负责文件的上传
<%--
  Created by IntelliJ IDEA.
  User: SWT
  Date: 2023/9/22
  Time: 18:39
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <form action="http://localhost:8080//jsp//uploadServlet"  method="post" enctype="multipart/form-data">
      姓名:<input type="text" name="username"><br>
       头像:<input type="file" name="photo"><br>
        <input type="submit" value="上传">


    </form>
</head>
<body>

</body>
</html>

  • 我们使用UploadServlet程序进行接收:
package com.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 {
   //用来出来上传的文件
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("文件上传过来了");
    }
}

  • doPost方法被访问,说明数据可以发送到servlet程序

文件上传的Http协议介绍

  • 在请求时
  • 请求头:
  • 请求体
  • 为什么用Post请求
    因为get请求是有长度限制的,而上传文件一般都会超出get的长度限制

    我们的客户端是以流的形式发送数据的,所以我们服务器必须以流的形式来接收数据
  • 以流的形式来接收数据:
  • 新的servlet程序
package com.servlet;

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

public class UploadServlet extends HttpServlet {
   //用来出来上传的文件
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

       // System.out.println("文件上传过来了");
        final ServletInputStream inputStream = req.getInputStream();
        byte[]buffer = new byte[1024000];
        final int read = inputStream.read(buffer);
        System.out.println(new String(buffer,0,read));


    }
}

  • 输出

上传用到的类和方法的介绍

  • 根据上面我们已经可以接收到文件的数据了,现在我们需要的是将数据解析得到我们需要的内容

  • 对于文件上传这些常见的功能,有很多第三方机构已经写好了,并且封装成了jar包,我们只需要区使用就可以了

  • 1.我们首先需要导入我们需要的2个jar包(在资料里面有)
    我们上传的表单项的类型不同,对应的我们的处理也不同(如普通表单项和文件)

对于具体的类和方法的介绍,详见官方笔记

使用fileupload解析上传数据

  • 注意:
  • jsp文件中的表单项
 <form action="http://localhost:8080//jsp//uploadServlet"  method="post" enctype="multipart/form-data">
      姓名:<input type="text" name="username"><br>
       头像:<input type="file" name="photo"><br>
        <input type="submit" value="上传">
  

    </form>
  • servlet程序
package com.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.ServletInputStream;
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 {
   //用来出来上传的文件
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

      //1.判断上传的数据是否是多段数据(只有是多段数据,才是文件上传的)
        if(ServletFileUpload.isMultipartContent((req))){
            //创建FileItemFactory工厂实现类
            FileItemFactory factory = new DiskFileItemFactory();
            //创建用于解析上传数据的工具类ServletFileUpload类的对象
            ServletFileUpload servletFileUpload = new ServletFileUpload(factory);

            try {
                //解析上传的数据,得到每一个表单项FileItem
                final List <FileItem>list = servletFileUpload.parseRequest(req);
                //循环判断,每一个表单项:是普通类型还是上传的文件
                for (FileItem fileItem : list) {
                    if(fileItem.isFormField()){
                        //普通表单项
                        System.out.println("表单项的name属性值:"+fileItem.getFieldName());
                        //参数:UTF-8解决乱码问题
                        System.out.println("表单项的value属性值:"+fileItem.getString("UTF-8"));
                    }else {
                      //上传的文件
                        System.out.println("表单项的name属性值:"+fileItem.getFieldName());
                        System.out.println("上传的文件名:"+fileItem.getName());
                        //将上传的文件写入到d盘的一个位置
                        fileItem.write(new File("d://"+fileItem.getName()));
                    }
                }
            } catch (FileUploadException e) {
               e.printStackTrace();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }


    }
}

  • 输出

文件下载

文件下载的实现

  • 关于文件下载过程:

  • 从上面我们可以看出,文件下载需要首先客户端发送请求,然后服务器进行处理:服务器会在服务器的存储系统中找到相应的文件。并以二进制的形式将文件返回给客户端。然后由客户端进行下载

  • 我们省略客户端,直接以访问servlet的形式进行演示

  • 步骤分析:

  • servlet程序
package com.servlet;

import org.apache.commons.io.IOUtils;

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

//处理下载图片
public class DownLoadServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

     //1.获取要下载的文件名
        String downloadFileName = "小姐姐.jpg";
        //2.读取要下载的文件内容(通过ServletContext对象可以读取)
        /*
        *斜杠被服务器解析为http://localhost:8080/工程名/
        * 映射到web目录
         */

        final ServletContext servletContext = getServletContext();
        final InputStream resourceAsStream = servletContext.getResourceAsStream("/file/" + downloadFileName);
        //4.在回传前,通过响应头告诉客户端返回的数据类型
        //获取要下载的文件类型
        final String mimeType = servletContext.getMimeType("/file/" + downloadFileName);
        System.out.println("下载的文件类型"+mimeType);
      //告诉客户端返回的数据类型
        resp.setContentType(mimeType);
        //5.告诉客户端收到的数据用于下载使用(还是使用响应头)
        //Content-Disposition响应头  ,表示收到数据怎样处理
        //attachment,表示附件,表示下载使用
        //filename表示指定下载的文件名
        resp.setHeader("Content-Disposition", "attachment;filename=" + downloadFileName);
        //获取输出流
        final ServletOutputStream servletOutputStream = resp.getOutputStream();
        //3.把下载的文件内容回传给客户端
       //读取输出流中的全部数据,复制给输出流,输出给客户端
        IOUtils.copy(resourceAsStream, servletOutputStream);






    }
}

我们的步骤4 5 需要在把下载的文件内容回传给客户端之前完成。

  • 我的文件名小姐姐.jpg是中文名。好像在下载的文件中中文名并没有显示。似乎是还存在中文乱码的问题

使用URLEncoder解决谷歌和IE浏览器中文下载名乱码的问题

  • 下载的文件名是由由设置的响应头决定的,可以和原来的文件名相同,也可以不同
    当我们 下载的文件名是中文的话,我们的下载的文件的名字,是不能展现的
  • 我们看看http协议里面的内容


因为http协议是老美设计的,在最初的时候仅仅考虑了英文的情况,没有考虑到中文的情况

使用URLEncoder类将中文进行URL编码。将中文转化为%xx%xx(16进制)的格式。已解决中文乱码的问题

  • 此时我们再次查看协议内容

    此时我们下载的文件的中文文件名也可以显示了
  • 但是好像通过上面的编码解决中文的问题,但是我们的火狐浏览器还是不支持URL编码

Base64编码操作

现在一般推荐使用Base64类直接进行编码和解码,而不去使用sun.misc.BASE64Encoder类

package com.test;

import java.util.Base64;
import java.util.Scanner;

public class Test2 {

//演示Base64的编码和解码
    public static void main(String[] args) {
        //编码操作
        String content = "使用Base64进行编码和解码";
        final Base64.Encoder encoder = Base64.getEncoder();
        final byte[] encode = encoder.encode(content.getBytes());
        System.out.println(encode.toString());
        //解码操作
        final Base64.Decoder decoder = Base64.getDecoder();
        final byte[] decode = decoder.decode(encode);
        System.out.println(new String(decode));

    }
}

使用Base64编码解决解决火狐浏览器下载(附件)中文乱码问题


目前我的IDEA上面提供Base64编码的类是Base64类,但是我们按照上面的写法无法达到相同的需求

使用 User-Agent请求头判断:动态切换不同的方案解决所有浏览器附件乱码的问题

  • 基本思路:如果是火狐浏览器使用Base64编码,如果是IE,谷歌使用URL编码
  • 在请求头中:有个User-Agent表示浏览器的信息

总结

1.在文件的下载阶段使用了URL和Base64两种方式来应对不同的浏览器来解决中文附件名乱码的问题。但是IDEA已经没有提供Base64EnCoder类来编码了。而是提供了Base64类,使用了Base64类但是采用Base64Encoder类的回应头的写法,好像不能生效
2.但是好像目前的火狐浏览器也可以兼容URL的编码方式了,所以我认为没有必要进行浏览器判断来选择不同的编码方式了,而是可以直接都使用URL的编码方式就可以了