Загрузка файла Resteasy multipart / data-form в GAE - PullRequest
7 голосов
/ 02 февраля 2011

Я пытаюсь использовать resteasy 2.0.1.GA для загрузки формы с файлом в приложении GAE, используя метод, рекомендованный в Как мне выполнить загрузку файла multipart / form с помощью jax-rs?

Index.html

<form action="/rest/upload" method="post" enctype="multipart/form-data">
  <input type="text" name="name" />
  <input type="file" name="file" />
  <input type="submit" />
</form>

Rest.java

@Path("")
public class Rest {
    @POST
    @Path("/rest/upload")
    @Consumes("multipart/form-data")
    public String postContent(@MultipartForm UploadForm form) {
        System.out.println(form.getData().length);
        System.out.println(form.getName());
        return "Done";
    }
}

UploadForm.java

public class UploadForm {

    private String name;
    private byte[] data;

    @FormParam("name")
    public void setPath(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @FormParam("file")
    public void setContentData(byte[] data) {
        this.data = data;
    }

    public byte[] getData() {
        return data;
    }
}

Но я получаю следующее сообщение об ошибке (возможно, из-за того, что поставщик RESTEasy использует временные файлы для обработки входного потока):

HTTP ERROR 500

Problem accessing /files/service/upload. Reason:

    java.io.FileOutputStream is a restricted class. Please see the Google  App Engine developer's guide for more details.

Caused by:

java.lang.NoClassDefFoundError: java.io.FileOutputStream is a restricted class. Please see the Google  App Engine developer's guide for more details.
    at com.google.appengine.tools.development.agent.runtime.Runtime.reject(Runtime.java:51)
    at org.apache.james.mime4j.storage.TempFileStorageProvider$TempFileStorageOutputStream.<init>(TempFileStorageProvider.java:117)
    at org.apache.james.mime4j.storage.TempFileStorageProvider.createStorageOutputStream(TempFileStorageProvider.java:107)
    at org.apache.james.mime4j.storage.ThresholdStorageProvider$ThresholdStorageOutputStream.write0(ThresholdStorageProvider.java:113)
    at org.apache.james.mime4j.storage.StorageOutputStream.write(StorageOutputStream.java:119)
    at org.apache.james.mime4j.codec.CodecUtil.copy(CodecUtil.java:43)
    at org.apache.james.mime4j.storage.AbstractStorageProvider.store(AbstractStorageProvider.java:57)
    at org.apache.james.mime4j.message.BodyFactory.textBody(BodyFactory.java:167)
    at org.apache.james.mime4j.message.MessageBuilder.body(MessageBuilder.java:148)
    at org.apache.james.mime4j.parser.MimeStreamParser.parse(MimeStreamParser.java:101)
    at org.apache.james.mime4j.message.Message.<init>(Message.java:141)
    at org.apache.james.mime4j.message.Message.<init>(Message.java:100)
    at org.jboss.resteasy.plugins.providers.multipart.MultipartInputImpl.parse(MultipartInputImpl.java:76)
    at org.jboss.resteasy.plugins.providers.multipart.MultipartFormAnnotationReader.readFrom(MultipartFormAnnotationReader.java:55)
    at org.jboss.resteasy.core.interception.MessageBodyReaderContextImpl.proceed(MessageBodyReaderContextImpl.java:105)
    at org.jboss.resteasy.plugins.interceptors.encoding.GZIPDecodingInterceptor.read(GZIPDecodingInterceptor.java:46)
    at org.jboss.resteasy.core.interception.MessageBodyReaderContextImpl.proceed(MessageBodyReaderContextImpl.java:108)
    at org.jboss.resteasy.core.messagebody.ReaderUtility.doRead(ReaderUtility.java:111)
    at org.jboss.resteasy.core.messagebody.ReaderUtility.doRead(ReaderUtility.java:93)
    at org.jboss.resteasy.core.MessageBodyParameterInjector.inject(MessageBodyParameterInjector.java:146)
    at org.jboss.resteasy.core.MethodInjectorImpl.injectArguments(MethodInjectorImpl.java:114)
    at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:137)
    at org.jboss.resteasy.core.ResourceMethod.invokeOnTarget(ResourceMethod.java:252)
    at org.jboss.resteasy.core.ResourceMethod.invoke(ResourceMethod.java:217)
    at org.jboss.resteasy.core.ResourceMethod.invoke(ResourceMethod.java:206)
    at org.jboss.resteasy.core.SynchronousDispatcher.getResponse(SynchronousDispatcher.java:514)
    at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:491)
    at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:120)
    at org.jboss.resteasy.plugins.server.servlet.ServletContainerDispatcher.service(ServletContainerDispatcher.java:200)
    at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:48)
    at org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher.service(HttpServletDispatcher.java:43)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
    ...

Кто-нибудь сталкивался с этой проблемой с GAE и RESTEasy?Кто-нибудь решил это?Я не мог найти упоминаний по этому вопросу нигде.Спасибо!

Ответы [ 6 ]

8 голосов
/ 29 апреля 2012

Я только столкнулся с этой проблемой и посмотрел в исходном коде конструктор сообщений mime4j.Он получает TempFileStorageProvider по телефону DefaultStorageProvider.getInstance().Вы можете изменить значение по умолчанию на то, которое не записывает в файловую систему, вызвав:

DefaultStorageProvider.setInstance(new MemoryStorageProvider());

Это org.apache.james.mime4j.storage.DefaultStorageProvider.

Спасибо за краткий пример использования @MultipartForm!

7 голосов
/ 03 февраля 2011

Ну, я нашел обходной путь - я использую Apache commons-upload с RESTEasy, вводя HttpServletRequest в метод RESTEasy (и преобразовывая потоки в байтовый массив / строку с помощью commons-IO) , Все пакеты поддерживаются движком приложения.

@Path("")
public class Rest {
    @POST
    @Path("/rest/upload")
    public String postContent(@Context HttpServletRequest request) {
        ServletFileUpload upload = new ServletFileUpload();
        FileItemIterator fileIterator = upload.getItemIterator(request);
        while (fileIterator.hasNext()) {
            FileItemStream item = fileIterator.next();
            if ("file".equals(item.getFieldName())){
                byte[] content = IOUtils.toByteArray(item.openStream())
                // Save content into datastore
                // ... 
            } else if ("name".equals(item.getFieldName())){
                String name=IOUtils.toString(item.openStream());
                // Do something with the name string
                // ...
            }
        }
        return "Done";
    } 
}

Я бы все же предпочел найти решение RESTEasy, чтобы избежать ненужного кода вокруг fileIterator.

1 голос
/ 24 августа 2015

Чтобы использовать MemoryStorageProvider с RESTEasy, вы можете установить следующее системное свойство:

-Dorg.apache.james.mime4j.defaultStorageProvider=org.apache.james.mime4j.storage.MemoryStorageProvider

Я пробовал это сделать с помощью RESTEasy 2.3.1.GA и jboss-as-7.1.0.Final.

В предыдущих версиях RESTEasy также была ошибка, когда временные файлы не удалялись (https://issues.jboss.org/browse/RESTEASY-681). Использование MemoryStorageProvider - это обходной путь.

1 голос
/ 02 февраля 2011

Похоже, библиотека mime4j пытается записать временные файлы, что не разрешено в движке приложения.mime4j может быть сконфигурирован для использования провайдера памяти, но я не знаю, позволяет ли RESTeasy его использовать.

0 голосов
/ 01 июня 2018

Я только что обновил jar resteasy-multipart-provider с 2.2.0.GA до 3.1.4.Final.Мы должны вызвать метод close явно.Он позаботится об удалении файлов m4jxxxx.tmp.

см. @Docs http://docs.jboss.org/resteasy/docs/3.1.4.Final/userguide/html_single/index.html

package org.jboss.resteasy.plugins.providers.multipart;
public interface MultipartInput {
List<InputPart> getParts();
String getPreamble();
// You must call close to delete any temporary files created
// Otherwise they will be deleted on garbage collection or on JVM exit
void close();
}
0 голосов
/ 19 августа 2017

Я пытался использовать MemoryStorageProvider.Но похоже, что он не работает для большинства файлов, кроме меньших.

Я предложил другое решение, которое расширяет AbstractStorageProvider с помощью облачного хранилища Google, и оно прекрасно работает.

https://gist.github.com/azimbabu/0aef75192c385c6d4461118583b6d22f

<code>import com.google.appengine.tools.cloudstorage.GcsFileOptions;
import com.google.appengine.tools.cloudstorage.GcsFilename;
import com.google.appengine.tools.cloudstorage.GcsInputChannel;
import com.google.appengine.tools.cloudstorage.GcsOutputChannel;
import com.google.appengine.tools.cloudstorage.GcsService;
import lombok.extern.slf4j.Slf4j;
import org.apache.james.mime4j.storage.AbstractStorageProvider;
import org.apache.james.mime4j.storage.Storage;
import org.apache.james.mime4j.storage.StorageOutputStream;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.Channels;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.UUID;

/**
 * A {@link org.apache.james.mime4j.storage.StorageProvider} that stores the data in google cloud storage files. The files
 * are stored in a user specified bucket. User of this class needs to supply the google cloud storage service and bucket name.
 *
 * This implementation is based on {@link org.apache.james.mime4j.storage.TempFileStorageProvider}
 * <p>
 * Example usage:
 *
 * <pre>
 * final String bucketName = "my-bucket";
 * DefaultStorageProvider.setInstance(new GcsStorageProvider(gcsService, bucketName));
 * 
* / @ Открытый класс Slf4j GcsStorageProvider extends AbstractStorageProvider {private static final int FETCH_SIZE_MB = 4 *1024* 1024;приватная статическая конечная строка String PUBLIC_READ = "public-read";private static final GcsFileOptions gcsFileOpts = new GcsFileOptions.Builder (). acl (PUBLIC_READ) .mimeType ("text / csv"). build ();приватный финал GcsService gcsService;приватный финал String bucketName;/ ** * Создает новый GcsStorageProvider, используя заданные * значения.* * @param gcsService - реализация {@link GcsService} * @param bucketName имени хранилища облачного хранилища Google для использования.* / public GcsStorageProvider (final GcsService gcsService, final String bucketName) {this.gcsService = gcsService;this.bucketName = bucketName;} @Override public StorageOutputStream createStorageOutputStream () throws IOException {return new GcsStorageProvider.GcsStorageOutputStream (gcsService, bucketName);} закрытый статический финальный класс GcsStorage реализует Storage {private final GcsService gcsService;личное GcsFilename gcsFilename;приватный статический финал Set filesToDelete = new HashSet ();public GcsStorage (final GcsService gcsService, final GcsFilename gcsFilename) {this.gcsService = gcsService;this.gcsFilename = gcsFilename;} @Override public InputStream getInputStream () создает IOException {if (this.gcsFilename == null) {throw new IllegalStateException («хранилище удалено»);} else {final GcsInputChannel readChannel = gcsService.openPrefetchingReadChannel (gcsFilename, 0, FETCH_SIZE_MB);return Channels.newInputStream (readChannel);}} @Override public void delete () {synchronized (filesToDelete) {if (this.gcsFilename! = Null) {filesToDelete.add (this.gcsFilename);this.gcsFilename = null;} final Iterator iterator = filesToDelete.iterator ();while (iterator.hasNext ()) {GcsFilename filename = (GcsFilename) iterator.next ();try {if (gcsService.delete (filename)) {iterator.remove ();}} catch (final IOException ex) {log.error (ex.getMessage (), ex);}}}}} частный статический конечный класс GcsStorageOutputStream extends StorageOutputStream {частный конечный GcsService gcsService;личное GcsFilename gcsFilename;приватный финал OutputStream outputStream;public GcsStorageOutputStream (final GcsService gcsService, final String bucketName) выдает IOException {this.gcsService = gcsService;final String fileName = UUID.randomUUID (). toString ();this.gcsFilename = new GcsFilename (bucketName, fileName);GcsOutputChannel gcsOutputChannel = gcsService.createOrReplace (gcsFilename, gcsFileOpts);this.outputStream = Channels.newOutputStream (gcsOutputChannel);} @Override защищенный void write0 (буфер byte [], смещение int, длина int) создает IOException {this.outputStream.write (буфер, смещение, длина);} @Override защищенное хранилище toStorage0 () выдает IOException {return new GcsStorage (gcsService, gcsFilename);} @Override public void close () создает IOException {super.close (); this.outputStream.close (); } } }
...