week12_day03_Fileupload

1.FileUpload

1.1.文件上传的准备工作
请求报文。文件的二进制信息应当放在哪?请求体。
form表单 method=post
input type=file

Upload.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/app/upload" method="post">
        <input type="text" name="username"><br>
        <input type="password" name="password"><br>
        <input type="file" name="image"><br>
        <input type="submit">
    </form>
</body>
</html>

正文长度为12,很明显不正常
在这里插入图片描述
此时发现,仅会上传文件名,不会上传真实的文件数据。
解决方案:form表单需要添加enctype=multipart/form-data
这次的正文长度就比较正常了
在这里插入图片描述
接下来,编写servlet来接收上传的文件。

UploadServlet:

package com.cskaoyan.upload;

import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

@WebServlet("/upload")
public class UploadServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //逻辑
        //response给我们提供好了现存的响应输出流
        //request里面有没有给我们封装好现存的输入流呢?
        ServletInputStream inputStream = request.getInputStream();
        String realPath = getServletContext().getRealPath("upload/1.txt");
        File file = new File(realPath);
        //防止父目录不存在,引发异常,如果父目录不存在,则创建
        if(!file.getParentFile().exists()){
           file.getParentFile().mkdirs();
        }
        FileOutputStream fileOutputStream = new FileOutputStream(file);
        int length = 0;
        byte[] bytes = new byte[1024];
        while ((length = inputStream.read(bytes)) != -1){
            fileOutputStream.write(bytes, 0, length);
        }
        fileOutputStream.close();
        //inputStream可以关也可以不关,不关的话tomcat会帮助我们关闭
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

上传完成后,开发目录中没有发现上传的文件,应当到部署目录中去查看上传的文件。
在这里插入图片描述
发现最终文件损坏?代码有问题吗????没有问题的。
可以更换一个文件,1.txt来看一下。
上传前:
在这里插入图片描述
上传后:
在这里插入图片描述
文件平白无故多出了一些WebKitFormBoundary分隔符,
对于一个普通的文本,多出一些内容
如果对于一个二进制文件,图片、音频、视频、exe文件,多出来这一部分会直接导致文件损坏。

1.2.文件上传遇到的第一个问题

1.2.1.会多出来一部分分隔符,导致文件损坏
在文件上传的同时,还想上传form表单数据
在这里插入图片描述
会发现获取到的数据都是null
此时上传的文件打开,会发现
在这里插入图片描述
普通的form表单数据也进入到文件中了。

1.2.2.无法获取到普通form表单请求参数,且表单数据会进入到上传的文件中
为什么呢?
所有一切的原因都是因为添加了enctype=multipart/form-data。但是如果不添加,就不会上传文件。
在这里插入图片描述
在这里插入图片描述

请求体中的数据结构发生了变更,之前的数据key=value&key=value拼接的,getParameter通过key可以获取到数据value,那么如果数据结构发生了变更,getParameter理应不能再用了。
为什么整个表单中的数据都会进入文件中呢?
因为这些数据都是位于请求体中的,request.getInputSream()相当于把整个请求体全部放入文件中。同样的,分隔符也是位于请求体中的,所以也会被写入文件中。

1.3.总结
问题的话,主要有两大类,
1.文件会有分隔符,导致文件损坏
2.获取请求参数的API不再适用,无法获取到普通form表单数据
原因都是因为添加了enctype=multipart/form-data,因为添加这个属性,使得请求体的数据结构发生了变更,所以获取请求参数的API不在适用,同时添加之后,会多出很多分隔符,所以文件中会包含有分隔符。

1.4.使用组件来进行文件上传
接下来的内容大家会觉得代码太多,而且不理解,记不住。不用去深究。
以后学习到Spring。Spring会帮助你把这些步骤全部封装好,所以今后啥也不用干。

Commons-FileUpload组件
http://commons.apache.org/proper/commons-fileupload/using.html
这个网站会有Commons-FileUpload的一些使用案例。

// Check that we have a file upload request
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
这句代码的作用就是用来检查request是否包含enctype=multipart/form-data.

实现步骤
1、创建DiskFileItemFactory对象,设置缓冲区大小和临时文件目录
2、使用DiskFileItemFactory 对象创建ServletFileUpload对象,并设置上传文件的大小限制。
3、调用ServletFileUpload.parseRequest方法解析request对象,得到一个保存了所有上传内容的List对象。
4、对list进行迭代,每迭代一个FileItem对象,调用其isFormField方法判断是否是上传文件
True 为普通表单字段,则调用getFieldName、getString方法得到字段名和字段值
False 为上传文件,则调用getInputStream方法得到数据输入流,从而读取上传数据。
编码实现文件上传

Tip:核心API—DiskFileItemFactory
DiskFileItemFactory 是创建 FileItem 对象的工厂,这个工厂类常用方法:
public DiskFileItemFactory(int sizeThreshold, java.io.File repository)
构造函数

public void setSizeThreshold(int sizeThreshold)
设置内存缓冲区的大小,默认值为10K。当上传文件大于缓冲区大小时, fileupload组件将使用临时文件缓存上传文件。

public void setRepository(java.io.File repository)
指定临时文件目录,默认值为System.getProperty(“java.io.tmpdir”).

Tip:核心API—ServletFileUpload
ServletFileUpload 负责处理上传的文件数据,并将表单中每个输入项封装成一个 FileItem 对象中。常用方法有:
boolean isMultipartContent(HttpServletRequest request)
判断上传表单是否为multipart/form-data类型
List parseRequest(HttpServletRequest request)
解析request对象,并把表单中的每一个输入项包装成一个fileItem 对象,并返回一个保存了所有FileItem的list集合。
setFileSizeMax(long fileSizeMax)
设置单个上传文件的最大值bytes
setSizeMax(long sizeMax)
设置上传文件总量的最大值(多个文件同时上传时文件最大值的总和)
setHeaderEncoding(java.lang.String encoding)
设置编码格式,解决上传文件名乱码问题

Tip:核心API—FileItem
FileItem 用来表示文件上传表单中的一个上传文件对象或者普通表单对象
boolean isFormField() 判断FileItem是一个文件上传对象还是普通表单对象
如果判断是一个普通表单对象
String getFieldName() 获得普通表单对象的name属性
String getString(String encoding) 获得普通表单对象的value属性
如果判断是一个文件上传对象
String getName() 获得上传文件的文件名(有些浏览器会携带客户端路径)
InputStream getInputStream() 获得上传文件的输入流
delete() 在关闭FileItem输入流后,删除临时文件

Tip:上传文件的存放问题
文件存放位置
为保证服务器安全,上传文件应保存在应用程序的WEB-INF目录下,或者不受WEB服务器管理的目录。
为防止多用户上传相同文件名的文件,而导致文件覆盖的情况发生,文件上传程序应保证上传文件具有唯一文件名。
为防止单个目录下文件过多,影响文件读写速度,处理上传文件的程序应根据可能的文件上传总量,选择合适的目录结构生成算法,将上传文件分散存储。

UploadServlet2:

package com.cskaoyan.upload;

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

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
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.io.UnsupportedEncodingException;
import java.util.Iterator;
import java.util.List;

@WebServlet("/upload2")
public class UploadServlet2 extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //因为提交的是multipart/form-data,所以响应正文的数据结构发生改变,无法通过以下代码解决乱码问题。
        //request.setCharacterEncoding("utf-8");
        response.setContentType("text/html;charset=utf-8");
        //post里面写代码
        //返回的是true表示有Multipart
        boolean result = ServletFileUpload.isMultipartContent(request);
        if(!result){
            response.getWriter().println("不包含上传的文件");
            return;
        }
        //result=true
        DiskFileItemFactory factory = new DiskFileItemFactory();
        ServletContext servletContext = getServletContext();
        File repository = (File) servletContext.getAttribute("javax.servlet.context.tempdir");
        factory.setRepository(repository);

        // Create a new file upload handler
        ServletFileUpload upload = new ServletFileUpload(factory);
        //如果上传的文件名中文乱码,可以这么解决
        //upload.setHeaderEncoding("utf-8");
        // 允许上传文件的最大大小为1024 bytes
        //upload.setFileSizeMax(1024);
        try {
            List<FileItem> items = upload.parseRequest(request);
            //对items进行迭代
            Iterator<FileItem> iterator = items.iterator();
            while (iterator.hasNext()){
                //每一个item其实就是一个有name属性的input框的封装
                FileItem item = iterator.next();
                if(item.isFormField()){
                    //当前是普通的form表单数据
                    processFormField(item);
                }else{
                   //当前是上传的文件
                   processUploadedFile(item);
                }
            }
        } catch (FileUploadException e) {
            e.printStackTrace();
        }
    }

    /**
     * 处理上传的文件逻辑
     * @param item
     */
    private void processUploadedFile(FileItem item) {
        String fieldName = item.getFieldName();
        String fileName = item.getName();
        System.out.println(fieldName + " === " + fileName);
        String realPath = getServletContext().getRealPath("upload/" + fileName);
        File file = new File(realPath);
        if(!file.getParentFile().exists()){
            file.getParentFile().mkdirs();
        }
        try {
            //保存文件到硬盘中
            item.write(file);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 处理普通的form表单数据
     * 只需要获取name和value值即可
     * 上传文件之后,不要再用getParameter去获取请求参数了
     * 有没有中文乱码问题呢?
     * @param item
     */
    private void processFormField(FileItem item) {
        String name = item.getFieldName();
        String value = null;
        try {
            //表示value用utf-8编码
            value = item.getString("utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        System.out.println(name + " = " + value);
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

1.4.1.form表单数据中文乱码
在这里插入图片描述
在这里插入图片描述

问题:
比如,还是像之前获取form表单数据一样,接下来需要将这些数据封装到一个bean中,然后保存到数据库,怎么操作简便一些???????


作业1: 在之前的注册页面的基础上,增加注册时传头像(可以想一想如何更加简便)
form.html

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>Title</title>
</head>
<body>
<!--action就表示你需要向哪个地址发起一个请求-->
<!--submit一个表单一个,就构建一个请求报文,由于post请求方式,所以在请求体中,-->
<!--参数的组织形式是这样的:username=xx&password=xx&gender=xx&-->
<!--所以我们就可以通过name取到用户所输入的值-->
<form action="/app/register" enctype="multipart/form-data" method="post">
   用户名:<input type="text" name="username"><br>
   上传头像:<input type="file" name="img"><br>
   密码:<input type="password" name="password"><br>
   性别:男<input type="radio" name="gender" value="male"><input type="radio" name="gender" value="female"><br>
   爱好:java<input type="checkbox" name="hobby" value="java">
   c++<input type="checkbox" name="hobby" value="c++">
   python<input type="checkbox" name="hobby" value="python"><br>
   简介<textarea name="description"></textarea><br>
   <input type="submit">
</form>
</body>
</html>

HomeworkServlet01 :

package com.cskaoyan.Homework;

import org.apache.commons.beanutils.BeanUtils;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;

/**
* @author shihao
* @create 2020-06-25 16:35
*/
@WebServlet("/register")
public class HomeworkServlet01 extends HttpServlet {
   /**
    * 需要注意的是,当引入文件上传之后,form表单中的数据通过request.getParameter方法获取不到数据
    * @param request
    * @param response
    * @throws ServletException
    * @throws IOException
    */
   protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       Map<String, Object> map = FileUploadUtils.parseRequest(request);
       User user = new User();
       try {
           BeanUtils.populate(user,map);
       } catch (IllegalAccessException e) {
           e.printStackTrace();
       } catch (InvocationTargetException e) {
           e.printStackTrace();
       }
       //将user保存到数据库中
       System.out.println(user);
       //hobby里面的数据直接将多个数据以,隔开的形式保存在数据库中即可
   }

   protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

   }
}

作业2: 实现多文件上传功能

uploadMultiFile.html:

<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="UTF-8">
   <title>Title</title>
</head>
<body>
   <form action="/app/multi" enctype="multipart/form-data" method="post">
       <input type="file" name="headImage"><br>
       <input type="file" name="detailImage"><br>
       <input type="submit">
   </form>
</body>
</html>

FileUploadUtils:

package com.cskaoyan.Homework;

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

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.*;

/**
* @author shihao
* @create 2020-06-25 17:03
*/
public class FileUploadUtils {

   private static String UPLOAD_PATH = "/upload";

   public static Map<String, Object> parseRequest(HttpServletRequest request) {
       // Create a factory for disk-based file items
       DiskFileItemFactory factory = new DiskFileItemFactory();

       // Configure a repository (to ensure a secure temp location is used)
       ServletContext servletContext = request.getServletContext();
       File repository = (File) servletContext.getAttribute("javax.servlet.context.tempdir");
       factory.setRepository(repository);

       // Create a new file upload handler
       ServletFileUpload upload = new ServletFileUpload(factory);
       HashMap<String, Object> result = new HashMap<>();

       //如果上传的文件名中文乱码,可以这么解决
       upload.setHeaderEncoding("utf-8");

       // Parse the request
       try {
           List<FileItem> items = upload.parseRequest(request);
           Iterator<FileItem> iterator = items.iterator();
           while (iterator.hasNext()) {
               FileItem item = iterator.next();
               if (item.isFormField()) {
                   processFormField(item, result);
               } else {
                   processUploadField(item, result, servletContext);
               }
           }
       } catch (FileUploadException e) {
           e.printStackTrace();
       } catch (UnsupportedEncodingException e) {
           e.printStackTrace();
       }

       return result;
   }

   /**
    * 处理上传文件的具体逻辑
    *
    * @param item
    * @param result         文件上传成功后路径保存在map中
    * @param servletContext
    */
   private static void processUploadField(FileItem item, HashMap<String, Object> result, ServletContext servletContext) {
       String fieldName = item.getFieldName();
       String fileName = item.getName();
       System.out.println(fieldName + " === " + fileName);
       String realPath = servletContext.getRealPath("upload/" + fileName);
       File file = new File(realPath);
       if (!file.getParentFile().exists()) {
           file.getParentFile().mkdirs();
       }
       try {
           //保存文件到硬盘中
           item.write(file);
       } catch (Exception e) {
           e.printStackTrace();
       }
       result.put(fileName, realPath);
   }

   /**
    * 处理普通的表单数据
    * 需要考虑到hobby是一个组合,需要考虑到后面覆盖前面的情况
    *
    * @param item
    * @param result 处理表单的数据保存再map中
    */
   private static void processFormField(FileItem item, HashMap<String, Object> result) throws UnsupportedEncodingException {
       String fieldName = item.getFieldName();
       if (result.get(fieldName) == null) {
           result.put(fieldName, item.getString("utf-8"));
           return;
       }
       result.put(fieldName, result.get(fieldName) + "," + item.getString("utf-8"));
   }
}

HomeworkServlet02:

package com.cskaoyan.Homework;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

/**
* @author shihao
* @create 2020-06-25 17:52
*/
@WebServlet("/multi")
public class HomeworkServlet02 extends HttpServlet {
   protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
       Map<String, Object> map = FileUploadUtils.parseRequest(request);
       System.out.println(map);
   }

   protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

   }
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-玫瑰少年-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值