Web文件上传
RFC1867协议:
RFC1867主要是在HTTP协议的基础上为INPUT标签增加了file属性,同时限定了Form的method必须为POST,ENCTYPE必须为multipart/form-data。
表单上传协议:
POST /web/upload.html HTTP/1.1
header-name: header-value
User-Agent: MyBrowser
Host: 127.0.0.1:9090
Proxy-Connection: Keep-Alive
Content-Length: 5989
Content-Type: multipart/form-data; boundary=----------------314159265358979323846
------------------314159265358979323846
Content-Disposition: form-data; name="file1"; filename="logo.gif"
Content-Type: application/octet-stream; charset=ISO-8859-1
Content-Transfer-Encoding: binary
--二进制内容(略)
------------------314159265358979323846
Content-Disposition: form-data; name="file2"; filename="logo.png"
Content-Type: application/octet-stream; charset=ISO-8859-1
Content-Transfer-Encoding: binary
--二进制内容(略)
------------------314159265358979323846--
服务器端Servlet + Apache FileUpload实现
pom.xml
<!-- 上传文件 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
Servlet类
package com.what21.servlet.upload;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.what21.servlet.upload.helper.HttpServletUploadHelper;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
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.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@WebServlet("/upload/form")
public class FormUploadServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Map<String, Object> dataMap = new HashMap<>();
dataMap.put("code", "ok");
// ===================================================================================//
// ===== 处理&保存文件
// ===================================================================================//
String tmpDir = "D:/Temp/Upload/tmp"; // 临时目录
String domain = ""; // 域
String function = "head"; // 功能
String storagePath = "D:/Temp/Upload/"; // 上传目录
List<HttpServletUploadHelper.Entity> entityList = null;
try {
List<FileItem> fileItemList = HttpServletUploadHelper.toFileItems(request, tmpDir, "UTF-8");
entityList = HttpServletUploadHelper.parseToEntitys(fileItemList, storagePath, domain, function);
dataMap.put("data", entityList);
} catch (Exception e) {
e.printStackTrace();
dataMap.put("code", "error");
dataMap.put("msg", e.getMessage());
}
// ===================================================================================//
// ===== 返回结果
// ===================================================================================//
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(dataMap);
response.setContentType("text/json;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
PrintWriter writer = response.getWriter();
writer.print(json);
writer.flush();
}
}
工具类
package com.what21.servlet.upload.helper;
import com.relops.snowflake.Snowflake;
import lombok.Data;
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.FileCleanerCleanup;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FileCleaningTracker;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public final class HttpServletUploadHelper {
// ===================================================================================//
// ===== header中操作
// ===================================================================================//
/**
* 从header中获取值
*
* @param request
* @param name
* @param defaultValue
* @return
*/
public static String getHeaderValue(HttpServletRequest request, String name, String defaultValue) {
String value = request.getHeader(name);
if (StringUtils.isNotEmpty(value)) {
return value;
}
return defaultValue;
}
// ===================================================================================//
// ===== 文件操作
// ===================================================================================//
/**
* 获取文件后缀
*
* @param name
* @return
*/
public static String getFileSuffix(String name) {
if (StringUtils.isNotEmpty(name)) {
if (name.contains(".")) {
return name.split("\\.")[1];
}
}
return "";
}
/**
* 交换保存
*
* @param inputStream
* @param filename
* @throws IOException
*/
public static File exchangeSave(InputStream inputStream, String filename) throws IOException {
File saveFile = new File(filename);
if (!saveFile.getParentFile().exists()) {
saveFile.getParentFile().mkdirs();
}
FileOutputStream output = new FileOutputStream(filename);
int r = -1;
byte[] bytes = new byte[512];
while ((r = inputStream.read(bytes)) != -1) {
output.write(bytes, 0, r);
}
output.flush();
output.close();
return saveFile;
}
// ===================================================================================//
// ===== 实体操作
// ===================================================================================//
/**
* 从header中取参数
*
* @param request
* @param domain
* @param function
* @return
* @throws IOException
*/
public static Entity createEntityWithHeader(HttpServletRequest request, String domain, String function) throws IOException {
Entity entity = new Entity();
entity.setId(getSnowflakeId());
entity.setModular(HttpServletUploadHelper.getHeaderValue(request, "modular", "communal"));
entity.setModel(HttpServletUploadHelper.getHeaderValue(request, "model", "common"));
entity.setName(HttpServletUploadHelper.getHeaderValue(request, "name", getRandomString(30)));
entity.setType(HttpServletUploadHelper.getHeaderValue(request, "type", "file"));
entity.setSuffix(HttpServletUploadHelper.getFileSuffix(entity.getName()));
entity.setFile(entity.getId() + "." + entity.getSuffix());
String accessAddress = "/" + entity.getModular() + "/" + entity.getModular();
String storageAddress = "/" + entity.getModular() + "/" + entity.getModular();
if (StringUtils.isNotEmpty(domain)) {
accessAddress = domain + "/" + accessAddress;
storageAddress = domain + "/" + storageAddress;
}
if (StringUtils.isNotEmpty(function)) {
accessAddress = accessAddress + "/" + function;
storageAddress = storageAddress + "/" + function;
}
accessAddress = accessAddress + "/" + entity.getId();
storageAddress = storageAddress + "/" + entity.getId();
entity.setAccessAddress(accessAddress);
entity.setStorageAddress(storageAddress);
entity.setStatus(0);
return entity;
}
// ===================================================================================//
// ===== 实体类
// ===================================================================================//
@Data
public static class Entity {
// 主键ID
private Long id;
// 系统一级模块
private String modular;
// 系统二级模块
private String model;
// 静态资源名称
private String name;
// 静态资源类型
private String type;
// 静态资源文件
private String file;
// 文件大小
private Long size;
// 静态资源后缀
private String suffix;
// 静态资源状态
private Integer status;
// 资源存储地址
private String storageAddress;
// 资源访问地址
private String accessAddress;
}
// ===================================================================================//
// ===== 工具操作
// ===================================================================================//
/**
* @return
*/
public static Long getSnowflakeId() {
return getSnowflakeId(1);
}
/**
* @param node
* @return
*/
public static Long getSnowflakeId(int node) {
Snowflake snowflake = new Snowflake(node);
return snowflake.next();
}
/**
* 获取随机字符串
*
* @param length
* @return
*/
public static String getRandomString(int length) {
String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i++) {
int number = random.nextInt(62);
sb.append(str.charAt(number));
}
return sb.toString();
}
// ===================================================================================//
// ===== 表单上传
// ===================================================================================//
/**
* @param request
* @param tmpDir
* @param encode
* @return
* @throws FileUploadException
*/
public static List<FileItem> toFileItems(HttpServletRequest request, String tmpDir, String encode) throws FileUploadException {
DiskFileItemFactory factory = new DiskFileItemFactory();
// 重新设置临时文件保存目录
factory.setRepository(new File(tmpDir));
// 设置文件清除追踪器,文件上传过程中产生的临时文件
FileCleaningTracker fileCleaningTracker = FileCleanerCleanup.getFileCleaningTracker(request.getServletContext());
factory.setFileCleaningTracker(fileCleaningTracker);
ServletFileUpload upload = new ServletFileUpload(factory);
// 设置字符编码UTF-8
upload.setHeaderEncoding(encode);
// 解析请求数据包
return upload.parseRequest(request);
}
/**
* @param fileItemList
* @param storagePath
* @param domain
* @param function
* @return
* @throws IOException
*/
public static List<Entity> parseToEntitys(List<FileItem> fileItemList, String storagePath, String domain, String function)
throws IOException {
List<Entity> entityList = new ArrayList<>();
// ===================================================================//
// =====模块化参数
// ===================================================================//
String modular = "communal";
String model = "common";
for (FileItem fileItem : fileItemList) {
String fieldName = fileItem.getFieldName();
if (fileItem.isFormField()) {
if ("modular".equals(fieldName)) {
modular = fileItem.getString();
} else if ("model".equals(fieldName)) {
model = fileItem.getString();
}
}
}
// ===================================================================//
// =====地址处理
// ===================================================================//
String accessAddress = "/" + modular + "/" + model;
String storageAddress = "/" + modular + "/" + model;
if (StringUtils.isNotEmpty(domain)) {
accessAddress = domain + "/" + accessAddress;
storageAddress = domain + "/" + storageAddress;
}
if (StringUtils.isNotEmpty(function)) {
accessAddress = accessAddress + "/" + function;
storageAddress = storageAddress + "/" + function;
}
// ===================================================================//
// =====保存并构建对象
// ===================================================================//
for (FileItem fileItem : fileItemList) {
String name = fileItem.getName();
if (!fileItem.isFormField() && fileItem.getSize() > 0) {
// 文件后缀
String fileExtension = FilenameUtils.getExtension(name);
// 文件名
String fileName = FilenameUtils.getName(name);
Entity entity = new Entity();
entity.setId(getSnowflakeId());
entity.setModular(modular);
entity.setModel(model);
entity.setName(fileName);
entity.setType(fileItem.getContentType());
entity.setSuffix(fileExtension);
entity.setFile(entity.getId() + "." + fileExtension);
entity.setSize(fileItem.getSize());
entity.setStatus(0);
entity.setAccessAddress(accessAddress + "/" + entity.getId());
entity.setStorageAddress(storageAddress + "/" + entity.getId());
// 保存文件
File saveFile = new File(storagePath + entity.getStorageAddress());
if (!saveFile.getParentFile().exists()) {
saveFile.getParentFile().mkdirs();
}
FileUtils.copyInputStreamToFile(fileItem.getInputStream(), saveFile);
entityList.add(entity);
}
}
return entityList;
}
// ===================================================================================//
// ===== The end
// ===================================================================================//
}
上传文件客户端测试
Postman测试
结果
{
"code": "ok",
"data": [
{
"id": 6905426757113876480,
"modular": "modular01",
"model": "model01",
"name": "001.jpg",
"type": "image/jpeg",
"file": "6905426757113876480.jpg",
"size": 4473,
"suffix": "jpg",
"status": 0,
"storageAddress": "/modular01/model01/head/6905426757113876480",
"accessAddress": "/modular01/model01/head/6905426757113876480"
},
{
"id": 6905426757130653696,
"modular": "modular01",
"model": "model01",
"name": "212.txt",
"type": "text/plain",
"file": "6905426757130653696.txt",
"size": 1003,
"suffix": "txt",
"status": 0,
"storageAddress": "/modular01/model01/head/6905426757130653696",
"accessAddress": "/modular01/model01/head/6905426757130653696"
}
]
}
表单测试页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Form表单上传</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/4.6.1/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container " style="width:600px;">
<span>
<h1>Form表单上传</h1>
</span>
<form method="post" action="http://localhost:9090/web/upload/form" enctype="multipart/form-data">
<input type="hidden" name="model" value="object" />
<div class="form-group">
<label for="address">上传地址:</label>
<input id="address" name="address" class="form-control" value="http://localhost:9090/web/upload/form" />
</div>
<div class="form-group">
<label for="modular">一级模块:</label>
<input id="modular" name="modular" class="form-control" value="modular"/>
</div>
<div class="form-group">
<label for="model">二级模块:</label>
<input id="model" name="model" class="form-control" value="model" />
</div>
<div class="form-group">
<label for="file1">上传文件1:</label>
<input id="file1" type="file" name="file1" value="" class="form-control" />
</div>
<div class="form-group">
<label for="file2">上传文件2:</label>
<input id="file2" type="file" name="file2" value="" class="form-control" />
</div>
<div class="form-group">
<label for="desc">描述:</label>
<textarea id="desc" name="desc" class="form-control" style="min-height: 200px;"></textarea>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" type="checkbox">记住我
</label>
</div>
<button type="reset" class="btn btn-secondary">取消</button>
<button type="submit" class="btn btn-danger">提交</button>
</form>
</div>
</body>
</html>
本文暂时没有评论,来添加一个吧(●'◡'●)