ajax formData 上传文件,Servlet 获取文件和参数的方式

文章讲述了在公司老旧项目中遇到的文件上传问题及其解决方案。前端使用formData通过ajax发送POST请求,后端如果是单纯的字符串参数可以通过request.getParameter获取,但当Content-Type为multipart/form-data时,需使用CommonsMultipartResolver解析或直接修改为application/x-www-form-urlencoded。同时,文章展示了如何在Servlet中同时处理文件和字符串参数,通过FileItem的isFormField()方法区分。
摘要由CSDN通过智能技术生成

公司项目框架比较老,遇到一个上传文件的问题,记录一下解决方式。

前端使用 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上传文件时,菜鸟教程给出的获取参数中的文件的方式如下:

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));
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

风间琉璃c

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

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

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

打赏作者

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

抵扣说明:

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

余额充值