How to implement FileUpload in embedded Jetty?

混江龙づ霸主 提交于 2019-12-03 00:48:16

As this is a multipart request, and you are uploading a "file" part, you need to grab the data using

request.getPart("userfile1");

For elements of your request that are not of type "file", for example input type="text", then you can access those properties through request.getParameter.

Just to note, Jetty 8.0.1 is quite old, the newest Jetty version (as of writing, 8.1.12) includes some important bugfixes to multipart handling.

If you upgrade your Jetty version, you will probably have to explicitly enable Multipart handling due to Jetty more strictly enforcing the Servlet 3.0 spec (https://bugs.eclipse.org/bugs/show_bug.cgi?id=395000), use the @MultipartConfig annotation if you are using servlets.

With handlers you have to manually add a Request.__MULTIPART_CONFIG_ELEMENT as an attribute to your request prior to calling getPart(s)

if (request.getContentType() != null && request.getContentType().startsWith("multipart/form-data")) {
  baseRequest.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, MULTI_PART_CONFIG);
}

This will allow you to parse the multipart requests, but the temp multipart files created will not be cleaned if you inject the config in this way. For servlets it is handled by a ServletRequestListener attached to the Request (see org.eclipse.jetty.server.Request#MultiPartCleanerListener).

So, what we do is have a HandlerWrapper early in our handler chain which adds the multipart config if needed and ensures the multipart files are cleaned after the request has finished. Example:

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

This can be used like:

MultipartConfigInjectionHandler multipartConfigInjectionHandler =
    new MultipartConfigInjectionHandler();

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

multipartConfigInjectionHandler.setHandler(collection);

server.setHandler(multipartConfigInjectionHandler);
aminator

The class MultiPartInputStreamParser is deprecated since jetty 9.4.11 it was replaced by MultiPartFormInputStream

the new code looks like this:

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

There is also an official example from the Jetty authors.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!