【问题标题】:Parse a String containing multipart/form-data request body in Java在 Java 中解析包含 multipart/form-data 请求正文的字符串
【发布时间】:2018-07-02 19:15:32
【问题描述】:

问题陈述

我认为标题说明了一切:我正在寻找解析包含 multipart/form-data HTTP 请求正文部分的 String 的方法。 IE。字符串的内容看起来像这样:

--xyzseparator-blah
Content-Disposition: form-data; name="param1"

hello, world
--xyzseparator-blah
Content-Disposition: form-data; name="param2"

42
--xyzseparator-blah
Content-Disposition: form-data; name="param3"

blah, blah, blah
--xyzseparator-blah--

我希望获得的是parameters 地图,或者类似的东西,像这样。

parameters.get("param1");    // returns "hello, world"
parameters.get("param2");    // returns "42"
parameters.get("param3");    // returns "blah, blah, blah"
parameters.keys();           // returns ["param1", "param2", "param3"]

更多标准

  • 最好不必提供分隔符(即在这种情况下为 xyzseparator-blah),但如果必须,我可以忍受。
  • 我正在寻找基于库的解决方案,可能来自主流库(如“Apache Commons”或类似的东西)。
  • 我想避免滚动自己的解决方案,但在当前阶段,恐怕我不得不这样做。原因:虽然上面的示例通过一些字符串操作来拆分/解析似乎微不足道,但真正的多部分请求主体可以有更多的标头。除此之外,我不想重新发明(更不用说重新测试!)轮子:)

替代解决方案

如果存在满足上述标准的解决方案,但其输入是 Apache HttpRequest,而不是 String,那也是可以接受的。 (基本上我确实收到了HttpRequest,但是我使用的内部库是这样构建的,它将这个请求的主体提取为字符串,并将其传递给负责进行解析的类。但是,如果需要,我也可以直接在HttpRequest 上工作。)

相关问题

无论我如何尝试通过 Google、在 SO 和其他论坛上找到答案,解决方案似乎总是使用 commons fileupload 来浏览各个部分。例如:hereherehereherehere... 但是,该解决方案中使用的parseRequest 方法需要RequestContext,而我没有(只有HttpRequest)。

上述一些答案中也提到了另一种方法,即从HttpServletRequest 获取参数(但同样,我只有HttpRequest)。

编辑:换句话说:我可以包含 Commons Fileupload(我可以访问它),但这对我没有帮助,因为我有一个 HttpRequest , Commons Fileupload 需要RequestContext。 (除非有一种简单的方法可以将HttpRequest 转换为RequestContext,我忽略了这一点。)

【问题讨论】:

  • 你能改变内容类型标题吗?如果是这样 - @BalusC 可能会让你在这里 Convenient way to parse incoming multipart form data parameters in a servlet
  • @Andreas:你能详细说明一下吗?我简要检查了 API,但我并没有真正看到会解析请求的类。另外,不清楚如何从HttpRequest...
  • @JGlass:是的,我看到了那个答案(第一个“这里”链接是下面的答案!),但正如我所说,我没有HttpServletRequest,所以它没有帮助我。
  • 你有什么框架?在没有至少一个已经在使用的主要 Web 框架的情况下解析这种输入感觉有点不合理。

标签: java apache http multipart


【解决方案1】:

您可以使用 Commons FileUpload 解析您的字符串,方法是将其包装在实现“org.apache.commons.fileupload.UploadContext”的类中,如下所示。

不过,出于几个原因,我建议您将 HttpRequest 包装在您提出的替代解决方案中。首先,使用字符串意味着整个多部分 POST 正文,包括文件内容,需要放入内存中。包装 HttpRequest 将允许您流式传输它,一次只在内存中使用一个小缓冲区。其次,如果没有 HttpRequest,您将需要嗅出多部分边界,该边界通常位于“Content-type”标头中(请参阅RFC1867)。

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUpload;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;

public class MultiPartStringParser implements org.apache.commons.fileupload.UploadContext {

    public static void main(String[] args) throws Exception {
        String s = new String(Files.readAllBytes(Paths.get(args[0])));
        MultiPartStringParser p = new MultiPartStringParser(s);
        for (String key : p.parameters.keySet()) {
            System.out.println(key + "=" + p.parameters.get(key));
        }
    }
    
    private String postBody;
    private String boundary;
    private Map<String, String> parameters = new HashMap<String, String>();
            
    public MultiPartStringParser(String postBody) throws Exception {
        this.postBody = postBody;
        // Sniff out the multpart boundary.
        this.boundary = postBody.substring(2, postBody.indexOf('\n')).trim();
        // Parse out the parameters.
        final FileItemFactory factory = new DiskFileItemFactory();
        FileUpload upload = new FileUpload(factory);
        List<FileItem> fileItems = upload.parseRequest(this);
        for (FileItem fileItem: fileItems) {
            if (fileItem.isFormField()){
                parameters.put(fileItem.getFieldName(), fileItem.getString());
            } // else it is an uploaded file
        }
    }
    
    public Map<String,String> getParameters() {
        return parameters;
    }

    // The methods below here are to implement the UploadContext interface.
    @Override
    public String getCharacterEncoding() {
        return "UTF-8"; // You should know the actual encoding.
    }
    
    // This is the deprecated method from RequestContext that unnecessarily
    // limits the length of the content to ~2GB by returning an int. 
    @Override
    public int getContentLength() {
        return -1; // Don't use this
    }

    @Override
    public String getContentType() {
        // Use the boundary that was sniffed out above.
        return "multipart/form-data, boundary=" + this.boundary;
    }

    @Override
    public InputStream getInputStream() throws IOException {
        return new ByteArrayInputStream(postBody.getBytes());
    }

    @Override
    public long contentLength() {
        return postBody.length();
    }
}

【讨论】:

  • 我在 java 希望导入 javax.servlet.http.HttpServletRequest 的 parseRequest 方法出现错误,但是我在您的示例中没有看到该导入,我不想像我那样导入它不要在其他任何地方使用它,它会引入另一个依赖项。另外我们没有使用HttpServletRequest而是RequestContext重载的方法,所以我们甚至没有使用它..您有什么建议吗?
  • @gabriel 此代码通过“org.apache.commons.fileupload.FileUpload”类对 HttpServleRequest 具有传递依赖。通常,您会使用像 [this post] (stackoverflow.com/questions/1370414/…) 中所述的 Maven 之类的构建工具,但您也可以只下载 jar,如果您愿意,也可以手动将它们放入类路径中。您不需要在此类中添加导入语句。
猜你喜欢
  • 1970-01-01
  • 2012-04-04
  • 2016-11-30
  • 2015-08-27
  • 2017-10-08
  • 2016-01-31
  • 2017-01-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多