You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从JavaScript向Java Servlet Filter传递带图片的表单数据?

问题:Multipart表单数据在Servlet Filter中获取结果均为null

我尝试通过JavaScript将包含图片文件的表单数据发送至Java Servlet Filter,AJAX请求代码如下:

function displayCard() {
  addProductBtn.disabled = true;
  event.preventDefault();
  
  if(checkProductName(productNameField,productNameError)
   && checkProductImage(productImageField,productImageError) 
   && checkDescription(productDescField,productDescError,counter)
   && checkPrice(productPriceField,productPriceError)
   && checkDiscount(productDiscountField,productDiscountError)) {
    
    var request = new XMLHttpRequest();
    request.open("POST","product/add",true);
    request.setRequestHeader("Content-Type","multipart/form-data");
    // Set the Content-Type header to multipart/form-data
    var formData = new FormData();
    formData.append("productName", productNameField.value.trim());
    formData.append("productImage", productImageField.files[0]);
    formData.append("productDescription", productDescField.value.trim());
    formData.append("productPrice", productPriceField.value.trim());
    formData.append("productDiscount", productDiscountField.value.trim());
    request.send(formData);

    request.onreadystatechange = function(){
      if(request.readyState === XMLHttpRequest.DONE) {
        if(request.status === 200){
            var data = JSON.parse(request.responseText);
            if(data.success){
                closeForm();
                createItemCard(data.success.id);
                showAlert("Added successfully!","#15A78C","#DFFFD8","#A0E4CB");
            }
            else{
                productNameError.innerText =data.errors.nameError;
                productImageError.innerText =data.errors.imageError;
                productDescError.innerText = data.errors.descriptionError;
                productPriceError.innerText = data.errors.priceError;
                productDiscountError.innerText = data.errors.discountError;
            }
        }else{
            ///Error message
            showAlert("Oops! Something went wrong.","#FFFFFF","#FF7C7C","#E52315");
        }
      }
    };
  }
  addProductBtn.disabled = false;
}

但在Java过滤器中获取表单信息时,得到的结果均为null,过滤器代码如下:

package com.clothify.filters;

import com.clothify.controller.ImageHandler;
import com.clothify.filters.supporter.ErrorTextGenerator;
import com.clothify.filters.supporter.Validator;
import org.json.JSONObject;

import javax.servlet.ServletException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.IOException;
import java.io.PrintWriter;

@MultipartConfig
public class ProductValidator implements Filter {
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)  {

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;

        String method = request.getMethod();
        PrintWriter out = null;
        if(method.equals("POST") || method.equals("PUT")) {
            try {
                out = response.getWriter();

                String productName = request.getParameter("productName");
                Part imageFile = request.getPart("productImage");
                String productDescription = request.getParameter("productDescription");
                String productPrice = request.getParameter("productPrice");
                String productDiscount = request.getParameter("productDiscount");
                byte[] imageBytes = ImageHandler.getImageBytes(imageFile);

                JSONObject result = new JSONObject();

                if (!Validator.isNameValid(productName)) {
                    result.put("nameError", ErrorTextGenerator.getNameErrorText(productName));
                }
                if (!Validator.isImageValid(imageBytes)) {
                    result.put("imageError", ErrorTextGenerator.getImageErrorText(imageBytes));
                }
                if (!Validator.isDescriptionValid(productDescription)) {
                    result.put("descriptionError", ErrorTextGenerator.getDescriptionErrorText(productDescription));
                }
                if (!Validator.isPriceValid(productPrice)) {
                    result.put("priceError", ErrorTextGenerator.getPriceErrorText(productPrice));
                }
                if (!Validator.isDiscountValid(productDiscount)) {
                    result.put("discountError", ErrorTextGenerator.getDiscountErrorText(productDiscount));
                }

                if (result.length() > 0) {
                    out.println("{\"result\": " + result + " }");
                }
                else{
                    double price = Double.parseDouble(productPrice);
                    int discount = Integer.parseInt(productDiscount);

                    request.setAttribute("productPrice",price);
                    request.setAttribute("productDiscount",discount);
                    request.setAttribute("productImage",imageBytes);
                }
            }
            catch (ServletException e){e.printStackTrace();}
            catch (IOException e){e.printStackTrace();}
            finally {
                if(out!=null){
                    out.close();
                }
            }
        }
        try {
            filterChain.doFilter(request,response);
        }
        catch (IOException e) {e.printStackTrace(); }
        catch (ServletException e) {e.printStackTrace();}
    }
}

浏览器网络面板显示请求载荷包含所有表单信息,但过滤器无法获取有效数据,求解决方法。


解决方案

1. 移除AJAX请求中手动设置的Content-Type头

当使用FormData发送请求时,浏览器会自动生成正确的multipart/form-data请求头,其中包含必要的boundary分隔符。手动设置Content-Type会丢失这个分隔符,导致服务器无法正确解析表单数据。

修正后的JS代码片段:

var request = new XMLHttpRequest();
request.open("POST","product/add",true);
// 移除下面这行代码
// request.setRequestHeader("Content-Type","multipart/form-data");
var formData = new FormData();
// ... 其余代码不变

2. 调整Filter中数据获取的顺序

对于multipart/form-data类型的请求,必须先调用request.getPart()或request.getParts()解析文件部分,才能通过request.getParameter()获取普通表单字段。因为普通字段也是作为multipart的一部分存在的,需要先触发解析。

修正后的Filter核心代码片段:

try {
    out = response.getWriter();

    // 先获取文件Part,触发multipart解析
    Part imageFile = request.getPart("productImage");
    // 再获取普通表单参数
    String productName = request.getParameter("productName");
    String productDescription = request.getParameter("productDescription");
    String productPrice = request.getParameter("productPrice");
    String productDiscount = request.getParameter("productDiscount");
    
    byte[] imageBytes = null;
    // 先判断imageFile是否为null,避免空指针
    if(imageFile != null && imageFile.getSize() > 0){
        imageBytes = ImageHandler.getImageBytes(imageFile);
    }

    // ... 其余校验逻辑不变
}

3. 确保@MultipartConfig配置生效

@MultipartConfig注解通常需要添加在对应的Servlet类上,而不是Filter上(Filter的该注解可能不生效)。如果你的product/add对应的Servlet没有添加该注解,需要补上:

@WebServlet("/product/add")
@MultipartConfig(
    fileSizeThreshold = 1024 * 1024 * 2, // 2MB
    maxFileSize = 1024 * 1024 * 10,      // 10MB
    maxRequestSize = 1024 * 1024 * 50    // 50MB
)
public class ProductAddServlet extends HttpServlet {
    // ... Servlet逻辑
}

如果使用XML配置(web.xml),可以添加如下配置:

<servlet>
    <servlet-name>ProductAddServlet</servlet-name>
    <servlet-class>com.clothify.servlet.ProductAddServlet</servlet-class>
    <multipart-config>
        <file-size-threshold>2097152</file-size-threshold>
        <max-file-size>10485760</max-file-size>
        <max-request-size>52428800</max-request-size>
    </multipart-config>
</servlet>
<servlet-mapping>
    <servlet-name>ProductAddServlet</servlet-name>
    <url-pattern>/product/add</url-pattern>
</servlet-mapping>

内容的提问来源于stack exchange,提问作者Sanjay Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 16:28:12