使用Servlet接收 el-upload上传的文件 第三方commons-fileUpload 经典原生JavaWeb element-ui

484 阅读4分钟

前言

最近在尝试修改老师教的JavaWeb旧MVC结构, 去掉JSP技术,改成前后端分离模式

前端使用Vue全家桶制作,UI用的element-ui组件,页面跳转使用vue-router路由控制,

登录验证使用JWT

后端保留原JavaWeb大部分结构,将control层全部变成Http网络接口供前端请求数据

所有api接口都要提供JWT制作的token数据才能成功访问

image.png

问题

但是制作过程中,需要有上传文件的功能,

一做才发现有点难度,

原生JavaWeb解决办法: 最原始的jsp项目,是用from表单,配置enctype属性为multipart/form-data 并且设置methoad为post,里面用input为file类型来传数据

在servlet用的是FileUpload组件来接收

前后端分离的ui组件遇到的问题: 但是现在用的是element-ui的el-upload标签来上传文件

对于这个底层原理并不是很清楚,尝试用 servlet来接收发现根本没被请求到

同时网上一大堆资料都是springBoot做的,又过于超纲

解决方式

后端

首先还是导入commons-fileupload 和 commons-io的pom坐标

因为我用的Maven搭JavaWeb项目,所以用的坐标

当然。你也可以直接找jar包导入

以下是pom坐标

<!--commons-文件上传下载-fileupload-io-->
<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>1.4</version>
</dependency>
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-io</artifactId>
  <version>1.3.2</version>
</dependency>

接着编写代码,接收数据

package com.icefish.control;


import com.alibaba.fastjson.JSONObject;
import com.icefish.bean.fastjsonParse.img.Img;
import com.icefish.utils.Init_Req_Resp_Config;
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.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;

@WebServlet("/UserPicture")
public class UserPicture extends HttpServlet {
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        Init_Req_Resp_Config.init(req, resp);

        // 创建磁盘项目工厂
        DiskFileItemFactory diskFileItemFactory = new DiskFileItemFactory();

        // 创建解析类
        ServletFileUpload servletFileUpload = new ServletFileUpload(diskFileItemFactory);

        try {
            // 获取请求中的属性
            List<FileItem> list = servletFileUpload.parseRequest(req);

            // 循环遍历属性中的值
            for (FileItem fileItem : list) {
                // 判断是否为文件属性
                if (fileItem.isFormField()) {
                    String name = fileItem.getFieldName();
                    String value = fileItem.getString();

                    System.out.println(name + "-" + value);
                } else {
                    // 获取文件的文件名
                    String fileName = fileItem.getName();
                    // 获取文件大小
                    long fileSize = fileItem.getSize();

                    System.out.println("文件名为" + fileName + ",文件大小为" + fileSize);

                    // 获取文件的输入流
                    InputStream inputStream = fileItem.getInputStream();

                    // 创建存放文件的路径
                    String path = getServletContext().getRealPath("/users_picture");
                    System.out.println(path);

                    // 创建输出流
                    OutputStream outputStream = new FileOutputStream(path + "\" + fileName);
                    int len = 0;
                    byte[] bytes = new byte[1024];
                    while ((len = inputStream.read(bytes)) != -1) {
                        // 输出流写入文件
                        outputStream.write(bytes, 0, len);
                    }

                    // 关闭输入输出流
                    inputStream.close();
                    outputStream.close();

                    // 返回图片url路径
                    String data = "http://192.168.123.53:8888" + req.getContextPath() + "/users_picture/" + fileName;
                    Img img = new Img(data);


// 这里使用阿里的fastjson,因为我要的效果是:后端返回 图片在服务器地址的json数据 
// json效果{"img":"http://192.168.123.53:8888/sports_shop_backend_war/users_picture/kzb68up.jpg"}
                    JSONObject jsonObject = new JSONObject();
                    // 转换成json
                    String backToBrowser = jsonObject.toJSONString(img);

                    resp.getWriter().println(backToBrowser);
                }
            }
        } catch (FileUploadException e) {
            e.printStackTrace();
        }

}

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

注意了,这里需要先自己创建好图片输出的文件夹,像下面这样,和你的jsp文件同级

最好在随便放点东西进去,不然一开始没有文件,智能的IDEA会认为这个文件夹没什么用,不给你打包出来

image.png

前端代码

el-upload组件

记得把action的内容情况

自己写一个:http-request="upload",自己用axios来发起请求

 <div>
      <el-upload class="avatar-uploader" 
         action="" 
         :http-request="upload" 
         :show-file-list="false"
         :on-success="handleAvatarSuccess" 
         :before-upload="beforeAvatarUpload">
         <img v-if="imageUrl" :src="imageUrl" class="avatar">
         <i v-else class="el-icon-plus avatar-uploader-icon"></i>
       </el-upload>
</div>
export default {
    data() {
        return {
            username: '',
            password: '',
            passwordCheck: '',
            remember: false,
            imageUrl: '',
            headerObj: {
                "Access-Control-Allow-Origin": "*"
            },
        }
    },
    methods: {

        upload(param) {
            const formData = new FormData()
            formData.append('file', param.file)
            const url = 'http://192.168.123.53:8888/sports_shop_backend_war/UserPicture'
            const headers = { 
                'Content-Type': 'multipart/form-data'
             }
             // axios.post的构造函数有顺序,不可乱
            this.$http.post(
                url,
                formData,
                headers
            ).then(data => {
                console.log(data.data.img)
                this.$message.success("图片上传成功")
                this.imageUrl = data.data.img
            }).catch(response => {
                console.log(response)
            })
    },
    gotoLogin() {
        this.$router.push("/login/coreLogin");
    },
    handleAvatarSuccess(res, file) {
        this.imageUrl = URL.createObjectURL(file.raw);
    },
    beforeAvatarUpload(file) {
        const isJPG = file.type === 'image/jpeg';
        const isLt2M = file.size / 1024 / 1024 < 2;

        if (!isJPG) {
            this.$message.error('上传头像图片只能是 JPG 格式!');
        }
        if (!isLt2M) {
            this.$message.error('上传头像图片大小不能超过 2MB!');
        }
        return isJPG && isLt2M;
    }
}
}

最终效果

image.png

image.png

学习记录-《Java技术之fileupload--黑马》

image.png

原servlet的request.getParameter()不能获取 类型为multipart/form-data的数据

他只能获取enctype为application/x-www-form-urlencoded类型的数据

以流的形式获取

可以用这个方法获取正文的输入流 request.getInputStream();

image.png

结果 image.png

总结:有点接触到原理了,可以用流的方式获取正文内容,后面再进一步解析。但是并不方便。

image.png

image.png

commons-fileupload

这个是apach的,专门搞文件上传下载 但是有一个缺陷就是,建议不能搞特别大的文件

可以去官网查看

image.png

先导入jar包

image.png

该组件工作流程 image.png

主要的类和主要的方法

DiskFileItemFactory类 : 产生存在磁盘上的FileItem工厂

他的常用方法:

image.png

image.png

文件上传的编写步骤

image.png

判断用户的请求是不是 mutlipart/form-data类型的

image.png

创建DiskFileItemFactory 并设置缓存和临时文件的存放目录

创建ServletFileUpload,核心解析类

得到 List FileItem

遍历

image.png

以上爆红的方法是还没定义,在下面写一下

image.png

初始化

image.png

image.png

image.png

继续添加代码,构建输出流

image.png

需要考虑的问题

如何保证服务器的安全

写一个jsp文件上传,并且猜出来文件服务器存放位置

image.png

然后访问上传的这个文件

卧槽,直接当成jsp执行了

image.png

狠一点还可以把服务器关机

image.png

解决方法 image.png

中文乱码问题

同一个文件夹下的同名文件被覆盖问题

上传文件分目录存储

image.png

image.png

image.png