嵌入式 Jetty HttpServer 不接受 mutipart/form-data
Embedded Jetty HttpServer not accepting mutipart/form-data
我正在为我们的 REST API 的嵌入式 Http 服务器从 Eclipse Jersey/Grizzly (2.33) 迁移到 Eclipse/Jetty (10.0.6),我可以'为了我的生活,让 multipart/form-data 上传正常工作。我承认我 不 精通 Jetty 配置,也不 Jersey/Grizzly 配置,我正在将旧代码与 Jetty 食谱中的最少样板拼凑在一起。
在这一点上,我很高兴能让服务器接受请求。我可以自己研究如何处理这些文件。我目前的主要目标是 而不是 必须立即重写数十个 servlets/handlers(因此使用 Jersey ServletContainer)。
这是服务器代码:
public static void start() throws Exception {
httpConfig = new HttpConfiguration();
HttpConnectionFactory http11 = new HttpConnectionFactory(httpConfig);
server = new Server();
ServerConnector connector = new ServerConnector(server, http11);
connector.setPort((cmdOptions.port < 0 ? 9998 : cmdOptions.port));
server.setConnectors(new Connector[] {connector});
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
HandlerList handlers = new HandlerList();
ServletHandler servletHandler = new ServletHandler();
// Set up the resources.common package as the handlers for the servlet
ServletHolder servletHolder = context.addServlet(ServletContainer.class, "/*");
servletHolder.setInitOrder(0);
servletHolder.setInitParameter("jersey.config.server.provider.packages", "resources.grizzly;resources.common");
servletHandler.addServlet(servletHolder);
// MultiPartConfig setup - to allow for ServletRequest.getParts() usage
Path multipartTmpDir = Paths.get("target", "multipart-tmp");
multipartTmpDir = CommonResFileManager.ensureDirExists(multipartTmpDir);
String location = multipartTmpDir.toString();
long maxFileSize = 10 * 1024 * 1024; // 10 MB
long maxRequestSize = 10 * 1024 * 1024; // 10 MB
int fileSizeThreshold = 64 * 1024; // 64 KB
MultipartConfigElement multipartConfig = new MultipartConfigElement(location, maxFileSize, maxRequestSize, fileSizeThreshold);
FilterHolder filterHolder;
filterHolder = context.addFilter(resources.jetty.SecurityFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
filterHolder.setAsyncSupported(true);
CorsHandler corsHandler = new CorsHandler();
corsHandler.setHandler(context);
UploadHandler uploadHandler = new UploadHandler("/G/uploadFolder", multipartConfig, multipartTmpDir);
handlers.addHandler(corsHandler);
handlers.addHandler(uploadHandler);
handlers.addHandler(servletHandler);
server.setHandler(handlers);
server.start();
}
感兴趣的资源是:
public class CommonResProject extends CommonResBase {
...
@POST @Path("uploadFolder")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.MULTIPART_FORM_DATA)
public String uploadFolder(final FormDataMultiPart multiPart)
{
Collection<Part> parts = null;
try {
parts = ((HttpServletRequest)request).getParts();
} catch (IOException | ServletException ex) {
Logger.getLogger(CommonResProject.class.getName()).log(Level.SEVERE, null, ex);
}
if(parts != null){
parts.stream().forEach(p -> System.out.println(p.getName() + " ["+p.getContentType()+"]: "+p.getSize()+" bytes"));
}
// projects is a POJO that actually does the fiddly bits with the uploaded files
boolean retVal = projects.uploadFolder(getDB(), getUserId(), multiPart);
return "{\"retVal\" : " + String.valueOf(retVal) + "}";
}
...
扩展了:
@Path("/GProject")
public class GProject extends CommonResProject
{
public GProject()
{
super();
resInterface = new GBaseRes(); // Must always do
}
public static void processParts(HttpServletRequest request, HttpServletResponse response, java.nio.file.Path outputDir) throws ServletException, IOException
{
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
PrintWriter out = response.getWriter();
for (Part part : request.getParts())
{
out.printf("Got Part[%s].size=%s%n", part.getName(), part.getSize());
out.printf("Got Part[%s].contentType=%s%n", part.getName(), part.getContentType());
out.printf("Got Part[%s].submittedFileName=%s%n", part.getName(), part.getSubmittedFileName());
String filename = part.getSubmittedFileName();
if (StringUtil.isNotBlank(filename))
{
// ensure we don't have "/" and ".." in the raw form.
filename = URLEncoder.encode(filename, "utf-8");
java.nio.file.Path outputFile = outputDir.resolve(filename);
try (InputStream inputStream = part.getInputStream();
OutputStream outputStream = Files.newOutputStream(outputFile, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING))
{
IO.copy(inputStream, outputStream);
out.printf("Saved Part[%s] to %s%n", part.getName(), outputFile.toString());
}
}
}
}
public static ServletContextHandler newServletUploadHandler(MultipartConfigElement multipartConfig, java.nio.file.Path outputDir) throws IOException
{
ServletContextHandler context = new ServletContextHandler();
SaveUploadServlet saveUploadServlet = new SaveUploadServlet(outputDir);
ServletHolder servletHolder = new ServletHolder(saveUploadServlet);
servletHolder.getRegistration().setMultipartConfig(multipartConfig);
context.addServlet(servletHolder, "/uploadFolder");
return context;
}
public static class SaveUploadServlet extends HttpServlet
{
private final java.nio.file.Path outputDir;
public SaveUploadServlet(java.nio.file.Path outputDir) throws IOException
{
this.outputDir = outputDir.resolve("servlet");
ensureDirExists(this.outputDir);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
processParts(request, response, outputDir);
}
}
public static class UploadHandler extends AbstractHandler
{
private final String contextPath;
private final MultipartConfigElement multipartConfig;
private final java.nio.file.Path outputDir;
public UploadHandler(String contextPath, MultipartConfigElement multipartConfig, java.nio.file.Path outputDir) throws IOException
{
super();
this.contextPath = contextPath;
this.multipartConfig = multipartConfig;
this.outputDir = outputDir.resolve("handler");
CommonResFileManager.ensureDirExists(this.outputDir);
}
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
if (!target.startsWith(contextPath))
{
// not meant for us, skip it.
return;
}
if (!request.getMethod().equalsIgnoreCase("POST"))
{
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
return;
}
// Ensure request knows about MultiPartConfigElement setup.
request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, multipartConfig);
// Process the request
processParts(request, response, outputDir);
//baseRequest.setHandled(true);
}
}
}
当我尝试上传一组文件时,整个过程会生成以下堆栈跟踪:
2021-09-13 12:58:17 SEVERE - resources.common.ResponseExceptionMapper toResponse -- HTTP 415 Unsupported Media Type
javax.ws.rs.NotSupportedException: HTTP 415 Unsupported Media Type
at org.glassfish.jersey.server.spi.internal.ParameterValueHelper.getParameterValues(ParameterValueHelper.java:75)
at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$AbstractMethodParamInvoker.getParamValues(JavaResourceMethodDispatcherProvider.java:109)
at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$TypeOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:219)
at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:79)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:475)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:397)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:81)
at org.glassfish.jersey.server.ServerRuntime.run(ServerRuntime.java:255)
at org.glassfish.jersey.internal.Errors.call(Errors.java:248)
at org.glassfish.jersey.internal.Errors.call(Errors.java:244)
at org.glassfish.jersey.internal.Errors.process(Errors.java:292)
at org.glassfish.jersey.internal.Errors.process(Errors.java:274)
at org.glassfish.jersey.internal.Errors.process(Errors.java:244)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:265)
at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:234)
at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:680)
at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:394)
at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:346)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:366)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:319)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:205)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:764)
at org.eclipse.jetty.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1619)
at resources.jetty.SecurityFilter.doFilter(SecurityFilter.java:232)
at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1594)
at org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:164)
at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1594)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:506)
at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1571)
at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1372)
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:463)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1544)
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1294)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
at resources.jetty.CorsHandler.handle(CorsHandler.java:30)
at org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:51)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
at org.eclipse.jetty.server.Server.handle(Server.java:562)
at org.eclipse.jetty.server.HttpChannel.lambda$handle[=14=](HttpChannel.java:406)
at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:663)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:398)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:282)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:319)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)
at org.eclipse.jetty.io.SocketChannelEndPoint.run(SocketChannelEndPoint.java:101)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:412)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:381)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:268)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.lambda$new[=14=](AdaptiveExecutionStrategy.java:138)
at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:378)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:894)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1038)
at java.base/java.lang.Thread.run(Thread.java:832)
Caused by: org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyReader not found for media type=multipart/form-data;boundary=----WebKitFormBoundary4K5nPFIDDwLPZAnk, type=class org.glassfish.jersey.media.multipart.FormDataMultiPart, genericType=class org.glassfish.jersey.media.multipart.FormDataMultiPart.
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.java:208)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:132)
at org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundReadFrom(MappableExceptionWrapperInterceptor.java:49)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:132)
at org.glassfish.jersey.message.internal.MessageBodyFactory.readFrom(MessageBodyFactory.java:1072)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:885)
at org.glassfish.jersey.server.ContainerRequest.readEntity(ContainerRequest.java:282)
at org.glassfish.jersey.server.internal.inject.EntityParamValueParamProvider$EntityValueSupplier.apply(EntityParamValueParamProvider.java:73)
at org.glassfish.jersey.server.internal.inject.EntityParamValueParamProvider$EntityValueSupplier.apply(EntityParamValueParamProvider.java:56)
at org.glassfish.jersey.server.spi.internal.ParamValueFactoryWithSource.apply(ParamValueFactoryWithSource.java:50)
at org.glassfish.jersey.server.spi.internal.ParameterValueHelper.getParameterValues(ParameterValueHelper.java:68)
首先,不要直接使用ServletHandler
。
仅使用 ServletContetHandler
和 ServletHolder
配置您需要的内容。
ServletHandler
是一个内部 class 并不意味着可以像那样直接使用。
特别是对于您试图强加的所有配置。
接下来,将 UploadHandler
转换为 normal/formal HttpServlet
并将其正确添加到 ServletContextHandler
(您甚至可以使用相同的 url-pattern就像你现在一样)。 ServletContext
在这里很重要(对于多部分),而您的 raw/naked UploadHandler
实际上并没有像您想象的那样处理多部分。
stacktrace 表明您在生成 stacktrace 的时间点没有将 Jetty 用于 multipart,这意味着它绕过了 UploadHandler
并且 Jersey 本身正在尝试处理 multipart 内容。这可能意味着您在 Jersey servlet 上指定了 MultiPartConfigElement
。
ServletHolder servletHolder = context.addServlet(ServletContainer.class, "/*");
servletHolder.setInitOrder(0);
servletHolder.setInitParameter("jersey.config.server.provider.packages",
"resources.grizzly;resources.common");
Path multipartTmpDir = Paths.get("target", "multipart-tmp");
multipartTmpDir = CommonResFileManager.ensureDirExists(multipartTmpDir);
String location = multipartTmpDir.toString();
long maxFileSize = 10 * 1024 * 1024; // 10 MB
long maxRequestSize = 10 * 1024 * 1024; // 10 MB
int fileSizeThreshold = 64 * 1024; // 64 KB
MultipartConfigElement multipartConfig = new MultipartConfigElement(location,
maxFileSize, maxRequestSize, fileSizeThreshold);
servletHolder.getRegistration().setMultipartConfig(multipartConfig);
我正在为我们的 REST API 的嵌入式 Http 服务器从 Eclipse Jersey/Grizzly (2.33) 迁移到 Eclipse/Jetty (10.0.6),我可以'为了我的生活,让 multipart/form-data 上传正常工作。我承认我 不 精通 Jetty 配置,也不 Jersey/Grizzly 配置,我正在将旧代码与 Jetty 食谱中的最少样板拼凑在一起。
在这一点上,我很高兴能让服务器接受请求。我可以自己研究如何处理这些文件。我目前的主要目标是 而不是 必须立即重写数十个 servlets/handlers(因此使用 Jersey ServletContainer)。
这是服务器代码:
public static void start() throws Exception {
httpConfig = new HttpConfiguration();
HttpConnectionFactory http11 = new HttpConnectionFactory(httpConfig);
server = new Server();
ServerConnector connector = new ServerConnector(server, http11);
connector.setPort((cmdOptions.port < 0 ? 9998 : cmdOptions.port));
server.setConnectors(new Connector[] {connector});
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
HandlerList handlers = new HandlerList();
ServletHandler servletHandler = new ServletHandler();
// Set up the resources.common package as the handlers for the servlet
ServletHolder servletHolder = context.addServlet(ServletContainer.class, "/*");
servletHolder.setInitOrder(0);
servletHolder.setInitParameter("jersey.config.server.provider.packages", "resources.grizzly;resources.common");
servletHandler.addServlet(servletHolder);
// MultiPartConfig setup - to allow for ServletRequest.getParts() usage
Path multipartTmpDir = Paths.get("target", "multipart-tmp");
multipartTmpDir = CommonResFileManager.ensureDirExists(multipartTmpDir);
String location = multipartTmpDir.toString();
long maxFileSize = 10 * 1024 * 1024; // 10 MB
long maxRequestSize = 10 * 1024 * 1024; // 10 MB
int fileSizeThreshold = 64 * 1024; // 64 KB
MultipartConfigElement multipartConfig = new MultipartConfigElement(location, maxFileSize, maxRequestSize, fileSizeThreshold);
FilterHolder filterHolder;
filterHolder = context.addFilter(resources.jetty.SecurityFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
filterHolder.setAsyncSupported(true);
CorsHandler corsHandler = new CorsHandler();
corsHandler.setHandler(context);
UploadHandler uploadHandler = new UploadHandler("/G/uploadFolder", multipartConfig, multipartTmpDir);
handlers.addHandler(corsHandler);
handlers.addHandler(uploadHandler);
handlers.addHandler(servletHandler);
server.setHandler(handlers);
server.start();
}
感兴趣的资源是:
public class CommonResProject extends CommonResBase {
...
@POST @Path("uploadFolder")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.MULTIPART_FORM_DATA)
public String uploadFolder(final FormDataMultiPart multiPart)
{
Collection<Part> parts = null;
try {
parts = ((HttpServletRequest)request).getParts();
} catch (IOException | ServletException ex) {
Logger.getLogger(CommonResProject.class.getName()).log(Level.SEVERE, null, ex);
}
if(parts != null){
parts.stream().forEach(p -> System.out.println(p.getName() + " ["+p.getContentType()+"]: "+p.getSize()+" bytes"));
}
// projects is a POJO that actually does the fiddly bits with the uploaded files
boolean retVal = projects.uploadFolder(getDB(), getUserId(), multiPart);
return "{\"retVal\" : " + String.valueOf(retVal) + "}";
}
...
扩展了:
@Path("/GProject")
public class GProject extends CommonResProject
{
public GProject()
{
super();
resInterface = new GBaseRes(); // Must always do
}
public static void processParts(HttpServletRequest request, HttpServletResponse response, java.nio.file.Path outputDir) throws ServletException, IOException
{
response.setContentType("text/plain");
response.setCharacterEncoding("utf-8");
PrintWriter out = response.getWriter();
for (Part part : request.getParts())
{
out.printf("Got Part[%s].size=%s%n", part.getName(), part.getSize());
out.printf("Got Part[%s].contentType=%s%n", part.getName(), part.getContentType());
out.printf("Got Part[%s].submittedFileName=%s%n", part.getName(), part.getSubmittedFileName());
String filename = part.getSubmittedFileName();
if (StringUtil.isNotBlank(filename))
{
// ensure we don't have "/" and ".." in the raw form.
filename = URLEncoder.encode(filename, "utf-8");
java.nio.file.Path outputFile = outputDir.resolve(filename);
try (InputStream inputStream = part.getInputStream();
OutputStream outputStream = Files.newOutputStream(outputFile, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING))
{
IO.copy(inputStream, outputStream);
out.printf("Saved Part[%s] to %s%n", part.getName(), outputFile.toString());
}
}
}
}
public static ServletContextHandler newServletUploadHandler(MultipartConfigElement multipartConfig, java.nio.file.Path outputDir) throws IOException
{
ServletContextHandler context = new ServletContextHandler();
SaveUploadServlet saveUploadServlet = new SaveUploadServlet(outputDir);
ServletHolder servletHolder = new ServletHolder(saveUploadServlet);
servletHolder.getRegistration().setMultipartConfig(multipartConfig);
context.addServlet(servletHolder, "/uploadFolder");
return context;
}
public static class SaveUploadServlet extends HttpServlet
{
private final java.nio.file.Path outputDir;
public SaveUploadServlet(java.nio.file.Path outputDir) throws IOException
{
this.outputDir = outputDir.resolve("servlet");
ensureDirExists(this.outputDir);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
processParts(request, response, outputDir);
}
}
public static class UploadHandler extends AbstractHandler
{
private final String contextPath;
private final MultipartConfigElement multipartConfig;
private final java.nio.file.Path outputDir;
public UploadHandler(String contextPath, MultipartConfigElement multipartConfig, java.nio.file.Path outputDir) throws IOException
{
super();
this.contextPath = contextPath;
this.multipartConfig = multipartConfig;
this.outputDir = outputDir.resolve("handler");
CommonResFileManager.ensureDirExists(this.outputDir);
}
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
if (!target.startsWith(contextPath))
{
// not meant for us, skip it.
return;
}
if (!request.getMethod().equalsIgnoreCase("POST"))
{
response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
return;
}
// Ensure request knows about MultiPartConfigElement setup.
request.setAttribute(Request.__MULTIPART_CONFIG_ELEMENT, multipartConfig);
// Process the request
processParts(request, response, outputDir);
//baseRequest.setHandled(true);
}
}
}
当我尝试上传一组文件时,整个过程会生成以下堆栈跟踪:
2021-09-13 12:58:17 SEVERE - resources.common.ResponseExceptionMapper toResponse -- HTTP 415 Unsupported Media Type
javax.ws.rs.NotSupportedException: HTTP 415 Unsupported Media Type
at org.glassfish.jersey.server.spi.internal.ParameterValueHelper.getParameterValues(ParameterValueHelper.java:75)
at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$AbstractMethodParamInvoker.getParamValues(JavaResourceMethodDispatcherProvider.java:109)
at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$TypeOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:219)
at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:79)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:475)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:397)
at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:81)
at org.glassfish.jersey.server.ServerRuntime.run(ServerRuntime.java:255)
at org.glassfish.jersey.internal.Errors.call(Errors.java:248)
at org.glassfish.jersey.internal.Errors.call(Errors.java:244)
at org.glassfish.jersey.internal.Errors.process(Errors.java:292)
at org.glassfish.jersey.internal.Errors.process(Errors.java:274)
at org.glassfish.jersey.internal.Errors.process(Errors.java:244)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:265)
at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:234)
at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:680)
at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:394)
at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:346)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:366)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:319)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:205)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:764)
at org.eclipse.jetty.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1619)
at resources.jetty.SecurityFilter.doFilter(SecurityFilter.java:232)
at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1594)
at org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:164)
at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1594)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:506)
at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1571)
at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1372)
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:463)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1544)
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1294)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
at resources.jetty.CorsHandler.handle(CorsHandler.java:30)
at org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:51)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
at org.eclipse.jetty.server.Server.handle(Server.java:562)
at org.eclipse.jetty.server.HttpChannel.lambda$handle[=14=](HttpChannel.java:406)
at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:663)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:398)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:282)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:319)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)
at org.eclipse.jetty.io.SocketChannelEndPoint.run(SocketChannelEndPoint.java:101)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.runTask(AdaptiveExecutionStrategy.java:412)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.consumeTask(AdaptiveExecutionStrategy.java:381)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.tryProduce(AdaptiveExecutionStrategy.java:268)
at org.eclipse.jetty.util.thread.strategy.AdaptiveExecutionStrategy.lambda$new[=14=](AdaptiveExecutionStrategy.java:138)
at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:378)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:894)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1038)
at java.base/java.lang.Thread.run(Thread.java:832)
Caused by: org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyReader not found for media type=multipart/form-data;boundary=----WebKitFormBoundary4K5nPFIDDwLPZAnk, type=class org.glassfish.jersey.media.multipart.FormDataMultiPart, genericType=class org.glassfish.jersey.media.multipart.FormDataMultiPart.
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$TerminalReaderInterceptor.aroundReadFrom(ReaderInterceptorExecutor.java:208)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:132)
at org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundReadFrom(MappableExceptionWrapperInterceptor.java:49)
at org.glassfish.jersey.message.internal.ReaderInterceptorExecutor.proceed(ReaderInterceptorExecutor.java:132)
at org.glassfish.jersey.message.internal.MessageBodyFactory.readFrom(MessageBodyFactory.java:1072)
at org.glassfish.jersey.message.internal.InboundMessageContext.readEntity(InboundMessageContext.java:885)
at org.glassfish.jersey.server.ContainerRequest.readEntity(ContainerRequest.java:282)
at org.glassfish.jersey.server.internal.inject.EntityParamValueParamProvider$EntityValueSupplier.apply(EntityParamValueParamProvider.java:73)
at org.glassfish.jersey.server.internal.inject.EntityParamValueParamProvider$EntityValueSupplier.apply(EntityParamValueParamProvider.java:56)
at org.glassfish.jersey.server.spi.internal.ParamValueFactoryWithSource.apply(ParamValueFactoryWithSource.java:50)
at org.glassfish.jersey.server.spi.internal.ParameterValueHelper.getParameterValues(ParameterValueHelper.java:68)
首先,不要直接使用ServletHandler
。
仅使用 ServletContetHandler
和 ServletHolder
配置您需要的内容。
ServletHandler
是一个内部 class 并不意味着可以像那样直接使用。
特别是对于您试图强加的所有配置。
接下来,将 UploadHandler
转换为 normal/formal HttpServlet
并将其正确添加到 ServletContextHandler
(您甚至可以使用相同的 url-pattern就像你现在一样)。 ServletContext
在这里很重要(对于多部分),而您的 raw/naked UploadHandler
实际上并没有像您想象的那样处理多部分。
stacktrace 表明您在生成 stacktrace 的时间点没有将 Jetty 用于 multipart,这意味着它绕过了 UploadHandler
并且 Jersey 本身正在尝试处理 multipart 内容。这可能意味着您在 Jersey servlet 上指定了 MultiPartConfigElement
。
ServletHolder servletHolder = context.addServlet(ServletContainer.class, "/*");
servletHolder.setInitOrder(0);
servletHolder.setInitParameter("jersey.config.server.provider.packages",
"resources.grizzly;resources.common");
Path multipartTmpDir = Paths.get("target", "multipart-tmp");
multipartTmpDir = CommonResFileManager.ensureDirExists(multipartTmpDir);
String location = multipartTmpDir.toString();
long maxFileSize = 10 * 1024 * 1024; // 10 MB
long maxRequestSize = 10 * 1024 * 1024; // 10 MB
int fileSizeThreshold = 64 * 1024; // 64 KB
MultipartConfigElement multipartConfig = new MultipartConfigElement(location,
maxFileSize, maxRequestSize, fileSizeThreshold);
servletHolder.getRegistration().setMultipartConfig(multipartConfig);