Multipart File Upload on Google Appengine using jersey-1.7

23 views
Skip to first unread message

Qamal Kosim-Satyaputra via StackOverflow

unread,
Feb 19, 2013, 6:58:33 AM2/19/13
to google-appengin...@googlegroups.com

I wrote an application on Google Appengine with Jersey to handle simple file uploading. This works fine when it was on jersey 1.2. In the later versions (current 1.7) @FormDataParam is introduced to handle multipart/form inputs. I am using jersey-multipart and the mimepull dependency. It seems that the new way of doing it is creating temporary files in appengine which we all know is illegal...

Am I missing something or doing something wrong here since Jersey is now supposedly compatible with AppEngine?

@POST 
@Path("upload") 
@Consumes(MediaType.MULTIPART_FORM_DATA) 
public void upload(@FormDataParam("file") InputStream in) { .... }

The above will fail when called with these exceptions...

/upload
java.lang.SecurityException: Unable to create temporary file
    at java.io.File.checkAndCreate(File.java:1778)
    at java.io.File.createTempFile(File.java:1870)
    at java.io.File.createTempFile(File.java:1907)
    at org.jvnet.mimepull.MemoryData.createNext(MemoryData.java:87)
    at org.jvnet.mimepull.Chunk.createNext(Chunk.java:59)
    at org.jvnet.mimepull.DataHead.addBody(DataHead.java:82)
    at org.jvnet.mimepull.MIMEPart.addBody(MIMEPart.java:192)
    at org.jvnet.mimepull.MIMEMessage.makeProgress(MIMEMessage.java:235)
    at org.jvnet.mimepull.MIMEMessage.parseAll(MIMEMessage.java:176)
    at org.jvnet.mimepull.MIMEMessage.getAttachments(MIMEMessage.java:101)
    at com.sun.jersey.multipart.impl.MultiPartReaderClientSide.readMultiPart(MultiPartReaderClientSide.java:177)
    at com.sun.jersey.multipart.impl.MultiPartReaderServerSide.readMultiPart(MultiPartReaderServerSide.java:80)
    at com.sun.jersey.multipart.impl.MultiPartReaderClientSide.readFrom(MultiPartReaderClientSide.java:139)
    at com.sun.jersey.multipart.impl.MultiPartReaderClientSide.readFrom(MultiPartReaderClientSide.java:77)
    at com.sun.jersey.spi.container.ContainerRequest.getEntity(ContainerRequest.java:474)
    at com.sun.jersey.spi.container.ContainerRequest.getEntity(ContainerRequest.java:538)

Anyone have a clue? Is there a way to do thing while preventing mimepull from creating the temporary file?



Please DO NOT REPLY directly to this email but go to StackOverflow:
http://stackoverflow.com/questions/6301973/multipart-file-upload-on-google-appengine-using-jersey-1-7

yves amsellem via StackOverflow

unread,
Feb 19, 2013, 6:58:35 AM2/19/13
to google-appengin...@googlegroups.com

For files beyond its default size, multipart will create a temporary file. To avoid this — creating a file is impossible on gae — you can create a jersey-multipart-config.properties file in the project's resources folder and add this line to it:

bufferThreshold = -1

Then, the code is the one you gave:

@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response post(@FormDataParam("file") InputStream stream, @FormDataParam("file") FormDataContentDisposition disposition) throws IOException {
  post(file, disposition.getFileName());
  return Response.ok().build();
}


Please DO NOT REPLY directly to this email but go to StackOverflow:
http://stackoverflow.com/questions/6301973/multipart-file-upload-on-google-appengine-using-jersey-1-7/6304378#6304378

bsorrentino via StackOverflow

unread,
Feb 19, 2013, 6:58:37 AM2/19/13
to google-appengin...@googlegroups.com

i've found solution to programmatically avoid to use temporary file creation (very useful for GAE implementation)

My solution consist of creating a new MultiPartReader Provider ... below my code


@Provider
@Consumes("multipart/*")
public class GaeMultiPartReader implements MessageBodyReader<MultiPart> {

final Log logger = org.apache.commons.logging.LogFactory.getLog(getClass());

private final Providers providers;

private final CloseableService closeableService;

private final MIMEConfig mimeConfig;

private String getFixedHeaderValue(Header h) {
    String result = h.getValue();

    if (h.getName().equals("Content-Disposition") && (result.indexOf("filename=") != -1)) {
        try {
            result = new String(result.getBytes(), "utf8");
        } catch (UnsupportedEncodingException e) {            
            final String msg = "Can't convert header \"Content-Disposition\" to UTF8 format.";
            logger.error(msg,e);
            throw new RuntimeException(msg);
        }
    }

    return result;
}

public GaeMultiPartReader(@Context Providers providers, @Context MultiPartConfig config,
    @Context CloseableService closeableService) {
    this.providers = providers;

    if (config == null) {
        final String msg = "The MultiPartConfig instance we expected is not present. "
            + "Have you registered the MultiPartConfigProvider class?";
        logger.error( msg );
        throw new IllegalArgumentException(msg);
    }
    this.closeableService = closeableService;

    mimeConfig = new MIMEConfig();
    //mimeConfig.setMemoryThreshold(config.getBufferThreshold());
    mimeConfig.setMemoryThreshold(-1L); // GAE FIX
}

@Override
public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
    return MultiPart.class.isAssignableFrom(type);
}

@Override
public MultiPart readFrom(Class<MultiPart> type, Type genericType, Annotation[] annotations, MediaType mediaType,
    MultivaluedMap<String, String> headers, InputStream stream) throws IOException, WebApplicationException {
    try {
        MIMEMessage mm = new MIMEMessage(stream, mediaType.getParameters().get("boundary"), mimeConfig);

        boolean formData = false;
        MultiPart multiPart = null;

        if (MediaTypes.typeEquals(mediaType, MediaType.MULTIPART_FORM_DATA_TYPE)) {
            multiPart = new FormDataMultiPart();
            formData = true;
        } else {
            multiPart = new MultiPart();
        }

        multiPart.setProviders(providers);

        if (!formData) {
            multiPart.setMediaType(mediaType);
        }

        for (MIMEPart mp : mm.getAttachments()) {
            BodyPart bodyPart = null;

            if (formData) {
                bodyPart = new FormDataBodyPart();
            } else {
                bodyPart = new BodyPart();
            }

            bodyPart.setProviders(providers);

            for (Header h : mp.getAllHeaders()) {
                bodyPart.getHeaders().add(h.getName(), getFixedHeaderValue(h));
            }

            try {
                String contentType = bodyPart.getHeaders().getFirst("Content-Type");

                if (contentType != null) {
                    bodyPart.setMediaType(MediaType.valueOf(contentType));
                }

                bodyPart.getContentDisposition();
            } catch (IllegalArgumentException ex) {
                logger.error( "readFrom error", ex );
                throw new WebApplicationException(ex, 400);
            }

            bodyPart.setEntity(new BodyPartEntity(mp));
            multiPart.getBodyParts().add(bodyPart);
        }

        if (closeableService != null) {
            closeableService.add(multiPart);
        }

        return multiPart;
    } catch (MIMEParsingException ex) {
        logger.error( "readFrom error", ex );
        throw new WebApplicationException(ex, 400);
    }
}

}



Please DO NOT REPLY directly to this email but go to StackOverflow:
http://stackoverflow.com/questions/6301973/multipart-file-upload-on-google-appengine-using-jersey-1-7/14956805#14956805
Reply all
Reply to author
Forward
0 new messages