前言
最近在尝试修改老师教的JavaWeb旧MVC结构, 去掉JSP技术,改成前后端分离模式
前端使用Vue全家桶制作,UI用的element-ui组件,页面跳转使用vue-router路由控制,
登录验证使用JWT
后端保留原JavaWeb大部分结构,将control层全部变成Http网络接口供前端请求数据
所有api接口都要提供JWT制作的token数据才能成功访问
问题
但是制作过程中,需要有上传文件的功能,
一做才发现有点难度,
原生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会认为这个文件夹没什么用,不给你打包出来
前端代码
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;
}
}
}
最终效果
学习记录-《Java技术之fileupload--黑马》
原servlet的request.getParameter()不能获取 类型为multipart/form-data的数据
他只能获取enctype为application/x-www-form-urlencoded类型的数据
以流的形式获取
可以用这个方法获取正文的输入流 request.getInputStream();
结果
总结:有点接触到原理了,可以用流的方式获取正文内容,后面再进一步解析。但是并不方便。
commons-fileupload
这个是apach的,专门搞文件上传下载 但是有一个缺陷就是,建议不能搞特别大的文件
可以去官网查看
先导入jar包
该组件工作流程
主要的类和主要的方法
DiskFileItemFactory类 : 产生存在磁盘上的FileItem工厂
他的常用方法:
文件上传的编写步骤
判断用户的请求是不是 mutlipart/form-data类型的
创建DiskFileItemFactory 并设置缓存和临时文件的存放目录
创建ServletFileUpload,核心解析类
得到 List FileItem
遍历
以上爆红的方法是还没定义,在下面写一下
初始化
继续添加代码,构建输出流
需要考虑的问题
如何保证服务器的安全
写一个jsp文件上传,并且猜出来文件服务器存放位置
然后访问上传的这个文件
卧槽,直接当成jsp执行了
狠一点还可以把服务器关机
解决方法
中文乱码问题
同一个文件夹下的同名文件被覆盖问题
上传文件分目录存储