File upload 如何在嵌入式Jetty中实现文件上传?

File upload 如何在嵌入式Jetty中实现文件上传?,file-upload,embedded-jetty,File Upload,Embedded Jetty,我正在编写一个android应用程序,包括一个Web服务器。因此,我使用嵌入式Jetty(8.0.1) 我想做的下一步是实现文件上传 HTML看起来是这样的,我认为是正确的: <form action=\"fileupload\" method=\"post\" enctype=\"multipart/form-data\"> <table> <tbody> <tr>

我正在编写一个android应用程序,包括一个Web服务器。因此,我使用嵌入式Jetty(8.0.1)

我想做的下一步是实现文件上传

HTML看起来是这样的,我认为是正确的:

<form action=\"fileupload\" method=\"post\" enctype=\"multipart/form-data\"> 
    <table>
        <tbody>
            <tr>
                <td><input type=\"file\" name=\"userfile1\" /></td>
            </tr>
            <tr>
                <td>
                     <input type=\"submit\" value=\"Submit\" /><input type=\"reset\" value=\"Reset\" />
                </td>
            </tr>
        </tbody>
    </table>
</form>
并具有以下功能:

request.getParameter()

但每次我收到一个空对象。我必须做什么?

由于这是一个多部分请求,并且您正在上载一个“文件”部分,因此需要使用

request.getPart("userfile1");
对于不是“file”类型的请求元素,例如input type=“text”,则可以通过
request.getParameter
访问这些属性

需要注意的是,Jetty 8.0.1非常旧,最新的Jetty版本(截至编写时,8.1.12)包括一些重要的多部分处理错误修复

如果升级Jetty版本,可能需要显式启用多部分处理,因为Jetty更严格地执行Servlet 3.0规范(),如果使用Servlet,请使用
@MultipartConfig
注释

对于处理程序,您必须在调用
getPart
之前手动添加
请求。\uuuu MULTIPART\u CONFIG\u元素
作为请求的属性

这将允许您解析多部分请求,但如果您以这种方式注入配置,则不会清除创建的临时多部分文件。对于servlet,它由附加到请求的ServletRequestListener处理(请参见org.eclipse.jetty.server.Request#MultiPartCleanerListener)

因此,我们要做的是在处理程序链的早期有一个HandlerWrapper,如果需要,它会添加多部分配置,并确保在请求完成后清理多部分文件。例如:

import java.io.IOException;

import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.util.MultiException;
import org.eclipse.jetty.util.MultiPartInputStreamParser;

/**
 * Handler that adds the multipart config to the request that passes through if
 * it is a multipart request.
 *
 * <p>
 * Jetty will only clean up the temp files generated by
 * {@link MultiPartInputStreamParser} in a servlet event callback when the
 * request is about to die but won't invoke it for a non-servlet (webapp)
 * handled request.
 *
 * <p>
 * MultipartConfigInjectionHandler ensures that the parts are deleted after the
 * {@link #handle(String, Request, HttpServletRequest, HttpServletResponse)}
 * method is called.
 *
 * <p>
 * Ensure that no other handlers sit above this handler which may wish to do
 * something with the multipart parts, as the saved parts will be deleted on the return
 * from
 * {@link #handle(String, Request, HttpServletRequest, HttpServletResponse)}.
 */
public class MultipartConfigInjectionHandler extends HandlerWrapper {
  public static final String MULTIPART_FORMDATA_TYPE = "multipart/form-data";

  private static final MultipartConfigElement MULTI_PART_CONFIG = new MultipartConfigElement(
      System.getProperty("java.io.tmpdir"));

  public static boolean isMultipartRequest(ServletRequest request) {
    return request.getContentType() != null
        && request.getContentType().startsWith(MULTIPART_FORMDATA_TYPE);
  }

  /**
   * If you want to have multipart support in your handler, call this method each time
   * your doHandle method is called (prior to calling getParameter).
   *
   * Servlet 3.0 include support for Multipart data with its
   * {@link HttpServletRequest#getPart(String)} & {@link HttpServletRequest#getParts()}
   * methods, but the spec says that before you can use getPart, you must have specified a
   * {@link MultipartConfigElement} for the Servlet.
   *
   * <p>
   * This is normally done through the use of the MultipartConfig annotation of the
   * servlet in question, however these annotations will not work when specified on
   * Handlers.
   *
   * <p>
   * The workaround for enabling Multipart support in handlers is to define the
   * MultipartConfig attribute for the request which in turn will be read out in the
   * getPart method.
   *
   * @see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=395000#c0">Jetty Bug
   *      tracker - Jetty annotation scanning problem (servlet workaround) </a>
   * @see <a href="http://dev.eclipse.org/mhonarc/lists/jetty-users/msg03294.html">Jetty
   *      users mailing list post.</a>
   */
  public static void enableMultipartSupport(HttpServletRequest request) {
    request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, MULTI_PART_CONFIG);
  }

  @Override
  public void handle(String target, Request baseRequest, HttpServletRequest request,
      HttpServletResponse response) throws IOException, ServletException {
    boolean multipartRequest = HttpMethod.POST.is(request.getMethod())
        && isMultipartRequest(request);
    if (multipartRequest) {
      enableMultipartSupport(request);
    }

    try {
      super.handle(target, baseRequest, request, response);
    } finally {
      if (multipartRequest) {
        MultiPartInputStreamParser multipartInputStream = (MultiPartInputStreamParser) request
            .getAttribute(Request.__MULTIPART_INPUT_STREAM);
        if (multipartInputStream != null) {
          try {
            // a multipart request to a servlet will have the parts cleaned up correctly, but
            // the repeated call to deleteParts() here will safely do nothing.
            multipartInputStream.deleteParts();
          } catch (MultiException e) {
//            LOG.error("Error while deleting multipart request parts", e);
          }
        }
      }
    }
  }
}

自jetty 9.4.11以来,类
MultiPartInputStreamParser
已被弃用 它被
MultiPartFormInputStream

新代码如下所示:

import java.io.IOException;

import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.MultiPartFormInputStream;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.util.MultiPartInputStreamParser;

/**
 * Handler that adds the multipart config to the request that passes through if
 * it is a multipart request.
 *
 * <p>
 * Jetty will only clean up the temp files generated by
 * {@link MultiPartInputStreamParser} in a servlet event callback when the
 * request is about to die but won't invoke it for a non-servlet (webapp)
 * handled request.
 *
 * <p>
 * MultipartConfigInjectionHandler ensures that the parts are deleted after the
 * {@link #handle(String, Request, HttpServletRequest, HttpServletResponse)}
 * method is called.
 *
 * <p>
 * Ensure that no other handlers sit above this handler which may wish to do
 * something with the multipart parts, as the saved parts will be deleted on the return
 * from
 * {@link #handle(String, Request, HttpServletRequest, HttpServletResponse)}.
 */
public class MultipartConfigInjectionHandler extends HandlerWrapper {
    public static final String MULTIPART_FORMDATA_TYPE = "multipart/form-data";

    private static final MultipartConfigElement MULTI_PART_CONFIG = new MultipartConfigElement(
                    System.getProperty("java.io.tmpdir"));

    public static boolean isMultipartRequest(ServletRequest request) {
        return request.getContentType() != null
                        && request.getContentType().startsWith(MULTIPART_FORMDATA_TYPE);
    }

    /**
     * If you want to have multipart support in your handler, call this method each time
     * your doHandle method is called (prior to calling getParameter).
     *
     * Servlet 3.0 include support for Multipart data with its
     * {@link HttpServletRequest#getPart(String)} & {@link HttpServletRequest#getParts()}
     * methods, but the spec says that before you can use getPart, you must have specified a
     * {@link MultipartConfigElement} for the Servlet.
     *
     * <p>
     * This is normally done through the use of the MultipartConfig annotation of the
     * servlet in question, however these annotations will not work when specified on
     * Handlers.
     *
     * <p>
     * The workaround for enabling Multipart support in handlers is to define the
     * MultipartConfig attribute for the request which in turn will be read out in the
     * getPart method.
     *
     * @see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=395000#c0">Jetty Bug
     *      tracker - Jetty annotation scanning problem (servlet workaround) </a>
     * @see <a href="http://dev.eclipse.org/mhonarc/lists/jetty-users/msg03294.html">Jetty
     *      users mailing list post.</a>
     */
    public static void enableMultipartSupport(HttpServletRequest request) {
        request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, MULTI_PART_CONFIG);
    }

    @Override
    public void handle(String target, Request baseRequest, HttpServletRequest request,
                    HttpServletResponse response) throws IOException, ServletException {
        boolean multipartRequest = HttpMethod.POST.is(request.getMethod())
                        && isMultipartRequest(request);
        if (multipartRequest) {
            enableMultipartSupport(request);
        }

        try {
            super.handle(target, baseRequest, request, response);
        } finally {
            if (multipartRequest) {
                String MULTIPART = "org.eclipse.jetty.servlet.MultiPartFile.multiPartInputStream";
                MultiPartFormInputStream multipartInputStream = (MultiPartFormInputStream) request.getAttribute( MULTIPART );
                if (multipartInputStream != null) {
                    multipartInputStream.deleteParts();
                }
            }
        }
    }
}
import java.io.IOException;
导入javax.servlet.MultipartConfigElement;
导入javax.servlet.ServletException;
导入javax.servlet.ServletRequest;
导入javax.servlet.http.HttpServletRequest;
导入javax.servlet.http.HttpServletResponse;
导入org.eclipse.jetty.http.HttpMethod;
导入org.eclipse.jetty.http.MultiPartFormInputStream;
导入org.eclipse.jetty.server.Request;
导入org.eclipse.jetty.server.handler.HandlerWrapper;
导入org.eclipse.jetty.util.MultiPartInputStreamParser;
/**
*将多部分配置添加到通过if的请求的处理程序
*这是一个多部分请求。
*
*
*Jetty将只清理由生成的临时文件
*当
*请求即将终止,但不会为非servlet(webapp)调用它
*已处理请求。
*
*
*MultipartConfigInjectionHandler确保在
*{@link#句柄(字符串,请求,HttpServletRequest,HttpServletResponse)}
*方法被调用。
*
*
*确保没有其他处理程序位于该处理程序之上,而该处理程序可能希望这样做
*与多部分零件有关的内容,因为保存的零件将在返回时删除
*从
*{@link#句柄(字符串,请求,HttpServletRequest,HttpServletResponse)}。
*/
公共类MultipartConfigInjectionHandler扩展了HandlerWrapper{
公共静态最终字符串MULTIPART\u FORMDATA\u TYPE=“MULTIPART/FORMDATA”;
私有静态最终MultipartConfigElement MULTI\u PART\u CONFIG=新的MultipartConfigElement(
getProperty(“java.io.tmpdir”);
公共静态布尔isMultipartRequest(ServletRequest){
返回请求。getContentType()!=null
&&request.getContentType().startWith(多部分格式数据类型);
}
/**
*如果希望处理程序中有多部分支持,请每次调用此方法
*将调用doHandle方法(在调用getParameter之前)。
*
*Servlet 3.0包括对多部分数据及其
*{@link-HttpServletRequest#getPart(String)}&{@link-HttpServletRequest#getParts()}
*方法,但规范规定,在使用getPart之前,必须指定
*Servlet的{@link multipartconfig}。
*
*
*这通常是通过使用
*然而,当在上指定时,这些注释将不起作用
*处理程序。
*
*
*在处理程序中启用多部分支持的解决方法是定义
*请求的MultipartConfig属性,该属性将在
*getPart方法。
*
*@见
*@见
*/
公共静态无效启用MultipartSupport(HttpServletRequest){
setAttribute(request.\uuuu MULTIPART\u CONFIG\u元素,MULTI\u PART\u CONFIG);
}
@凌驾
公共无效句柄(字符串目标、请求baseRequest、HttpServletRequest、,
HttpServletResponse响应)引发IOException,ServletException{
布尔multipartRequest=HttpMethod.POST.is(request.getMethod())
&&isMultipartRequest(请求);
如果(多部分请求){
启用多部件支持(请求);
}
试一试{
超级句柄(目标、baseRequest、请求、响应);
}最后{
如果(多部分请求){
String MULTIPART=“org.eclipse.jetty.servlet.MultiPartFile.multiPartInputStream”;
MultiPartFormInputStream multipartInputStream=(MultiPartFormInputStream)请求.getAttribute(MULTIPART);
if(multipartInputStream!=null){
multipartInputStream.deleteParts();
}
}
}
}
}

还有来自Jetty作者的回复。

这个回复对我很有帮助。对eclipse bug的引用很有帮助,但这篇文章更有用,因为它提供了更多的细节:消息列表注释实际上是mi的一个注释
import java.io.IOException;

import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.util.MultiException;
import org.eclipse.jetty.util.MultiPartInputStreamParser;

/**
 * Handler that adds the multipart config to the request that passes through if
 * it is a multipart request.
 *
 * <p>
 * Jetty will only clean up the temp files generated by
 * {@link MultiPartInputStreamParser} in a servlet event callback when the
 * request is about to die but won't invoke it for a non-servlet (webapp)
 * handled request.
 *
 * <p>
 * MultipartConfigInjectionHandler ensures that the parts are deleted after the
 * {@link #handle(String, Request, HttpServletRequest, HttpServletResponse)}
 * method is called.
 *
 * <p>
 * Ensure that no other handlers sit above this handler which may wish to do
 * something with the multipart parts, as the saved parts will be deleted on the return
 * from
 * {@link #handle(String, Request, HttpServletRequest, HttpServletResponse)}.
 */
public class MultipartConfigInjectionHandler extends HandlerWrapper {
  public static final String MULTIPART_FORMDATA_TYPE = "multipart/form-data";

  private static final MultipartConfigElement MULTI_PART_CONFIG = new MultipartConfigElement(
      System.getProperty("java.io.tmpdir"));

  public static boolean isMultipartRequest(ServletRequest request) {
    return request.getContentType() != null
        && request.getContentType().startsWith(MULTIPART_FORMDATA_TYPE);
  }

  /**
   * If you want to have multipart support in your handler, call this method each time
   * your doHandle method is called (prior to calling getParameter).
   *
   * Servlet 3.0 include support for Multipart data with its
   * {@link HttpServletRequest#getPart(String)} & {@link HttpServletRequest#getParts()}
   * methods, but the spec says that before you can use getPart, you must have specified a
   * {@link MultipartConfigElement} for the Servlet.
   *
   * <p>
   * This is normally done through the use of the MultipartConfig annotation of the
   * servlet in question, however these annotations will not work when specified on
   * Handlers.
   *
   * <p>
   * The workaround for enabling Multipart support in handlers is to define the
   * MultipartConfig attribute for the request which in turn will be read out in the
   * getPart method.
   *
   * @see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=395000#c0">Jetty Bug
   *      tracker - Jetty annotation scanning problem (servlet workaround) </a>
   * @see <a href="http://dev.eclipse.org/mhonarc/lists/jetty-users/msg03294.html">Jetty
   *      users mailing list post.</a>
   */
  public static void enableMultipartSupport(HttpServletRequest request) {
    request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, MULTI_PART_CONFIG);
  }

  @Override
  public void handle(String target, Request baseRequest, HttpServletRequest request,
      HttpServletResponse response) throws IOException, ServletException {
    boolean multipartRequest = HttpMethod.POST.is(request.getMethod())
        && isMultipartRequest(request);
    if (multipartRequest) {
      enableMultipartSupport(request);
    }

    try {
      super.handle(target, baseRequest, request, response);
    } finally {
      if (multipartRequest) {
        MultiPartInputStreamParser multipartInputStream = (MultiPartInputStreamParser) request
            .getAttribute(Request.__MULTIPART_INPUT_STREAM);
        if (multipartInputStream != null) {
          try {
            // a multipart request to a servlet will have the parts cleaned up correctly, but
            // the repeated call to deleteParts() here will safely do nothing.
            multipartInputStream.deleteParts();
          } catch (MultiException e) {
//            LOG.error("Error while deleting multipart request parts", e);
          }
        }
      }
    }
  }
}
MultipartConfigInjectionHandler multipartConfigInjectionHandler =
    new MultipartConfigInjectionHandler();

HandlerCollection collection = new HandlerCollection();
collection.addHandler(new SomeHandler());
collection.addHandler(new SomeOtherHandler());

multipartConfigInjectionHandler.setHandler(collection);

server.setHandler(multipartConfigInjectionHandler);
import java.io.IOException;

import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.MultiPartFormInputStream;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.HandlerWrapper;
import org.eclipse.jetty.util.MultiPartInputStreamParser;

/**
 * Handler that adds the multipart config to the request that passes through if
 * it is a multipart request.
 *
 * <p>
 * Jetty will only clean up the temp files generated by
 * {@link MultiPartInputStreamParser} in a servlet event callback when the
 * request is about to die but won't invoke it for a non-servlet (webapp)
 * handled request.
 *
 * <p>
 * MultipartConfigInjectionHandler ensures that the parts are deleted after the
 * {@link #handle(String, Request, HttpServletRequest, HttpServletResponse)}
 * method is called.
 *
 * <p>
 * Ensure that no other handlers sit above this handler which may wish to do
 * something with the multipart parts, as the saved parts will be deleted on the return
 * from
 * {@link #handle(String, Request, HttpServletRequest, HttpServletResponse)}.
 */
public class MultipartConfigInjectionHandler extends HandlerWrapper {
    public static final String MULTIPART_FORMDATA_TYPE = "multipart/form-data";

    private static final MultipartConfigElement MULTI_PART_CONFIG = new MultipartConfigElement(
                    System.getProperty("java.io.tmpdir"));

    public static boolean isMultipartRequest(ServletRequest request) {
        return request.getContentType() != null
                        && request.getContentType().startsWith(MULTIPART_FORMDATA_TYPE);
    }

    /**
     * If you want to have multipart support in your handler, call this method each time
     * your doHandle method is called (prior to calling getParameter).
     *
     * Servlet 3.0 include support for Multipart data with its
     * {@link HttpServletRequest#getPart(String)} & {@link HttpServletRequest#getParts()}
     * methods, but the spec says that before you can use getPart, you must have specified a
     * {@link MultipartConfigElement} for the Servlet.
     *
     * <p>
     * This is normally done through the use of the MultipartConfig annotation of the
     * servlet in question, however these annotations will not work when specified on
     * Handlers.
     *
     * <p>
     * The workaround for enabling Multipart support in handlers is to define the
     * MultipartConfig attribute for the request which in turn will be read out in the
     * getPart method.
     *
     * @see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=395000#c0">Jetty Bug
     *      tracker - Jetty annotation scanning problem (servlet workaround) </a>
     * @see <a href="http://dev.eclipse.org/mhonarc/lists/jetty-users/msg03294.html">Jetty
     *      users mailing list post.</a>
     */
    public static void enableMultipartSupport(HttpServletRequest request) {
        request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, MULTI_PART_CONFIG);
    }

    @Override
    public void handle(String target, Request baseRequest, HttpServletRequest request,
                    HttpServletResponse response) throws IOException, ServletException {
        boolean multipartRequest = HttpMethod.POST.is(request.getMethod())
                        && isMultipartRequest(request);
        if (multipartRequest) {
            enableMultipartSupport(request);
        }

        try {
            super.handle(target, baseRequest, request, response);
        } finally {
            if (multipartRequest) {
                String MULTIPART = "org.eclipse.jetty.servlet.MultiPartFile.multiPartInputStream";
                MultiPartFormInputStream multipartInputStream = (MultiPartFormInputStream) request.getAttribute( MULTIPART );
                if (multipartInputStream != null) {
                    multipartInputStream.deleteParts();
                }
            }
        }
    }
}