Как реализовать FileUpload во встроенном Jetty?

Я пишу приложение для Android, включая веб-сервер. Поэтому я использую Embedded 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>

Когда я использую эту форму, я вижу в logcat, что я получил файл, но я не могу получить доступ к этому файлу в своем сервлете.

я попробовал это с

File file1 = (File) request.getAttribute( "userfile1" );

и со следующей функцией:

request.getParameter()

Но каждый раз, когда я получаю объект NULL. Что я должен сделать?


person Thomas Kay    schedule 15.07.2013    source источник


Ответы (2)


Поскольку это составной запрос, и вы загружаете «файловую» часть, вам нужно получить данные, используя

request.getPart("userfile1");

Для элементов вашего запроса, которые не имеют тип «файл», например тип ввода = «текст», вы можете получить доступ к этим свойствам через request.getParameter.

Просто отметим, что Jetty 8.0.1 довольно старая, новейшая версия Jetty (на момент написания, 8.1.12) включает в себя несколько важных исправлений для многокомпонентной обработки.

Если вы обновите свою версию Jetty, вам, вероятно, придется явно включить обработку Multipart, поскольку Jetty более строго соблюдает спецификацию Servlet 3.0 (https://bugs.eclipse.org/bugs/show_bug.cgi?id=395000), используйте аннотацию @MultipartConfig, если вы используете сервлеты.

С обработчиками вы должны вручную добавить Request.__MULTIPART_CONFIG_ELEMENT в качестве атрибута к вашему запросу перед вызовом getPart(s)

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

Это позволит вам анализировать составные запросы, но созданные временные составные файлы не будут очищены, если вы введете конфигурацию таким образом. Для сервлетов это обрабатывается 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);
          }
        }
      }
    }
  }
}

Это можно использовать как:

MultipartConfigInjectionHandler multipartConfigInjectionHandler =
    new MultipartConfigInjectionHandler();

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

multipartConfigInjectionHandler.setHandler(collection);

server.setHandler(multipartConfigInjectionHandler);
person Andrew    schedule 08.08.2013
comment
Этот ответ был полезен для меня. Ссылка на ошибку eclipse была полезной, но этот пост был еще полезнее, так как содержал больше деталей: dev.eclipse.org/mhonarc/lists/jetty-users/msg03294.html - person mwhidden; 09.08.2014
comment
Этот комментарий к списку сообщений на самом деле один из моих :) Я обновил ответ здесь, добавив дополнительную информацию об очистке составных временных файлов. - person Andrew; 12.12.2014
comment
@Andrew, не могли бы вы опубликовать минимальный рабочий пример загрузки с использованием встроенного причала. Я не могу поверить, что это было проблемой в течение 6 лет. Заставляет меня полностью потерять интерес к пристани - person Dr Deo; 25.07.2019

Класс MultiPartInputStreamParser устарел, так как причал 9.4.11 был заменен на 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();
                }
            }
        }
    }
}

Существует также ссылка официальный пример от авторов Jetty.

person aminator    schedule 27.07.2018
comment
MultiPartFormInputStream возвращал значение null. Но мне удалось получить MultiParts.MultiPartsUtilParser multiparts=(MultiParts.MultiPartsUtilParser)request.getAttribute(Request.__MULTIPARTS); Затем я использовал это, чтобы получить доступ к частям и delete их.for(Part p: multiparts.getParts()){p.delete();} - person Dr Deo; 08.08.2019