如何从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
相关产品推荐
相关产品推荐

