公司项目框架比较老,遇到一个上传文件的问题,记录一下解决方式。
前端使用 formData 封装数据,通过ajax发送post请求上传文件:
let formData = new FormData();
// 向formData中添加数据
formData.append("id", this.userHelperForm.id);
formData.append("name", this.userHelperForm.name);
formData.append("imageUrl", this.userHelperForm.imageUrl);
formData.append("file", this.fileList[0].raw);
formData.append("type", this.userHelperForm.type);
formData.forEach((value, key) => {
console.log("key %s: value %s", key, value);
});
let url = "UserHelperSubmitServlet";
$.ajax({
url: url,
type: "POST",
cache: false,
data: formData,
processData: false,
contentType: false,
})
.done(function (res) {
console.log(res);
})
.fail(function (res) {});
这里 contentType 的值设置为 false 就行,似乎它的值会默认转为:multipart/form-data
主要问题在后端,
如果参数中不传文件,即 Content-Type="application/x-www-form-urlencoded" 时,可以直接使用 request.getParamter() 方法获取参数。
如果参数中不传文件,但是 Content-Type="multipart/form-data"
也可以通过以下方式去获取参数(当然最好还是直接改成 application/x-www-form-urlencoded 即可解决问题):
String contentType = request.getContentType();
if (contentType != null && contentType.contains("multipart/form-data")) {
MultipartResolver resolver = new CommonsMultipartResolver(request.getSession().getServletContext());
//将转化后的 request 放入过滤链中
request = resolver.resolveMultipart(request);
}
在自己的Servlet方法中,先使用上面的代码,将request转换一次后,即可通过 request.getParameter(xxx) 方法去获取参数了。
但是如果参数中既有字符串需要传递,又有文件需要传递怎么办?
使用Servlet上传文件时,菜鸟教程给出的获取参数中的文件的方式如下:
核心代码如下:
//创建磁盘工厂对象
DiskFileItemFactory factory = new DiskFileItemFactory();
//Servlet文件上传核心对象
ServletFileUpload fileUpload = new ServletFileUpload(factory);
List<FileItem> fileItems = fileUpload.parseRequest(request);
拿到 fileItem以后,再通过InputStream上传文件,这里给出一段代码样例:
将拿到的 FileItem(即文件)作为参数传递到下面的方法进行上传。
public String saveFile(FileItem file) {
//判空
if (file == null) {
throw new SmartbiException(SJMHErrorCode.UPLOAD_FiLE_EMPTY);
} else {
String dateStr = dateFormat.format(new Date());
String uuid = UUIDGenerator.generate();
//文件名 日期+UUID+后缀名 eg: 2023_03_09_18_30_26_I40287aa50186785f785f1dfb018678642d930014.pdf
String fileName = timeFormat.format(new Date()) + "_" + uuid + "." + FileUtil.getSuffix(file.getName());
StringBuilder filePath = new StringBuilder();
String nasPath = NasUtil.getPath();
//路径格式 path/2023-03-09
filePath.append(nasPath)
.append(nasPath.endsWith(File.separator) ? "" : File.separator)
.append(dateStr);
try {
file.getInputStream();
} catch (IOException e) {
logger.error("获取不了");
throw new RuntimeException(e);
}
//上传文件 全路径eg:path/2023-03-09/2023_03_09_18_30_26_I40287aa50186785f785f1dfb018678642d930014.pdf
try {
NasUtil.uploadFile(file.getInputStream(), filePath.toString(), fileName);
} catch (Exception e) {
throw new SmartbiException(SJMHErrorCode.UPLOAD_FILE_FAIL, e);
}
//保存文件上传记录
saveUploadFileRecord(uuid, fileName, filePath.toString());
logger.info("上传成功!! 文件路径:" + filePath);
return uuid;
}
}
如果要同时获取文件和参数,就需要直接遍历FileItem,
通过 fileItem.isFormField() 来判断这个参数是字符串参数,还是文件参数。
public class UserHelperSubmitServlet extends HttpServlet {
private static final Logger logger = LoggerFactory.getLogger(UserHelperSubmitServlet.class);
private static final UserHelperService userHelperService = UserHelperService.getInstance();
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
try {
//创建磁盘工厂对象
DiskFileItemFactory factory = new DiskFileItemFactory();
//Servlet文件上传核心对象
ServletFileUpload fileUpload = new ServletFileUpload(factory);
List<FileItem> fileItems = fileUpload.parseRequest(request);
Map<String, Object> params = new HashMap<>();
//只能在这里获取参数 因为getParameter、getInputStream和getReader是互斥的,当流被其中一种方式读取后可能字节发生了改变,这时候用另外一种方法无论如何都获取不到的
//所以在 fileUpload.parseRequest(request) 之后 即时用下方注释掉的代码处理request后 再尝试使用 request.getParameter去获取参数 会发现也都是null
for (FileItem fileItem : fileItems) {
//是普通表单字段
if (fileItem.isFormField()) {
logger.info(fileItem.getFieldName() + " - " + fileItem.getString("UTF-8"));
params.put(fileItem.getFieldName(), fileItem.getString("UTF-8"));
} else {
//是文件 这里只上传一个文件 所以无需List处理
params.put(fileItem.getFieldName(), fileItem);
logger.info(fileItem.getName());
}
}
// Content-Type="application/x-www-form-urlencoded"是默认的编码方式 如果是这种编码方式 可以使用 request.getParameter 获取参数信息
// 由于前端是表单提交 formData包含文件 即 Content-Type="multipart/form-data" 所以正常使用 request.getParameter无法获取参数 会发现都是null
// 这一段代码的作用就是用来处理formData,处理后就可以正常使用 request.getParameter 获取字段信息,但是如果其中有文件 就无法获取文件 所以没使用这个方案
// String contentType = request.getContentType();
// if (contentType != null && contentType.contains("multipart/form-data")) {
// MultipartResolver resolver = new CommonsMultipartResolver(request.getSession().getServletContext());
// // 将转化后的 request 放入过滤链中
// request = resolver.resolveMultipart(request);
// }
String id = (String) params.get("id");
String name = (String) params.get("name");
String imageUrl = (String) params.get("imageUrl");
int type = Integer.parseInt((String) params.get("type"));
FileItem uploadFile = (FileItem) params.get("file");
userHelperService.saveOrUpdate(id, name, imageUrl, uploadFile, type);
Result result = new Result(0, "文件上传成功!");
response.getOutputStream()
.write(JSONObject.fromObject(result).toString().getBytes(StandardCharsets.UTF_8));
} catch (Exception e) {
logger.error(e.getMessage(), e);
Result result = new Result(-1, "文件上传失败," + e.getMessage());
response.getOutputStream().write(JSONObject.fromObject(result).toString().getBytes(StandardCharsets.UTF_8));
}
}
}