krasserm / grails-jaxrs

JAX-RS Plugin for Grails
http://code.google.com/p/grails-jaxrs/
Apache License 2.0
50 stars 48 forks source link

FileUpload with grails-jaxrs #52

Closed confile closed 10 years ago

confile commented 10 years ago

Does this plugin also support FileUpload with REST?

I an normal Grails controler action you would do something like this:

CommonsMultipartFile file = (CommonsMultipartFile) request.getFile('file')

How to I do fileupload in a rest resource to get the CommonsMultipartFile?

steffimueller commented 10 years ago

+1 I am interested in this answer too

davidecavestro commented 10 years ago

Sorry I nave no access to code now. However I've got uploads working simply following Jersey 1.x tutorials

confile commented 10 years ago

Could you post some code how to get the CommonsMultipartFile?

michaelrice commented 10 years ago

Here is one way of doing it:

    @POST
    @Path('/upload')
    Response upload(InputStream data) {
        StringBuffer content = new StringBuffer()
        // Max file size allowed (32 MiB).
        int limit = (32 * 1024 * 1024)

        // The current number of bytes read.
        int current = 0

        // The buffer (reads in chunks of 8 KiB).
        char[] chunk = new char[(8 * 1024)]

        // Number of bytes to project to detect limit breach.
        int byteProjection
        // Reader.
        Reader reader = new BufferedReader(new InputStreamReader(data, "UTF-8"))
        // Read from data.
        while ((byteProjection = reader.read(chunk)) != -1) {

            // If we project that this read has breached the limit, reject.
            if (current + byteProjection > limit) {
                // throw bad request here   
            }    

            content.append(chunk, 0, byteProjection)
            current += byteProjection
        }
        // what ever...
confile commented 10 years ago

grreat thank you

noamt commented 10 years ago

Closing the issue if an answer was provided

confile commented 10 years ago

yes thank you

confile commented 10 years ago

I tried to Upload a file using the following resource:

@POST
@Produces('application/json')
UploadDto upload(
        @Context HttpServletRequest request,
        @QueryParam("cookie") String cookie) {

    def contentType
    byte [] fileBytes

    log.debug "upload - cookie: "+cookie

    try{
        if (request instanceof MultipartHttpServletRequest) {
            log.debug "request instanceof MultipartHttpServletRequest"

            MultipartHttpServletRequest myrequest = request
            CommonsMultipartFile file = (CommonsMultipartFile) myrequest.getFile('file')
            fileBytes = file.bytes
            contentType = file.contentType
            log.debug ">>>>> upload size of the file in byte: "+ file.size
        }
        else if (request instanceof SecurityContextHolderAwareRequestWrapper) {
            log.debug "request instanceof SecurityContextHolderAwareRequestWrapper"

            SecurityContextHolderAwareRequestWrapper myrequest = request

            //get uploaded file's inputStream
            InputStream inputStream = myrequest.inputStream

            fileBytes = IOUtils.toByteArray(inputStream);
            contentType = myrequest.getHeader("Content-Type")
            log.debug ">>>>> upload size of the file in byte: "+ fileBytes.size()
        }
        else {
            log.error "request is not a MultipartHttpServletRequest or SecurityContextHolderAwareRequestWrapper"
            println "request: "+request.class
        }
    }
    catch (IOException e) {
        log.error("upload() failed to save file error: ", e)
    }
}

When I use the code in my Grails Controller it worked well but when I use it in a REST Resource I always get: request is not a MultipartHttpServletRequest or SecurityContextHolderAwareRequestWrapper

The send a file blob from JavaScript I use XMLHttpRequest which contains the blob in the body and some query parameters.

Did I miss something?

davidecavestro commented 10 years ago

@confile usually the following is enough for my needs

@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadMyFile(
    @FormDataParam('file') InputStream fileInputStream,
    @FormDataParam('file') FormDataContentDisposition contentDispositionHeader
    ...){...}

I've never tried sending the file as a blob in the body of a XHR. Maybe the plugin controller is consuming the request, returning an instance of another type, btw you missed to tell us the actual request type.

confile commented 10 years ago

The request type is POST. How do I get query parameter from the request?

Here is how I send the file from JavaScript. I use this to make sending possible cross devices:

    var str2ab_blobreader = function(str, callback) {
        var blob;
        BlobBuilder = window.MozBlobBuilder || window.WebKitBlobBuilder
                || window.BlobBuilder;
        if (typeof (BlobBuilder) !== 'undefined') {
            var bb = new BlobBuilder();
            bb.append(str);
            blob = bb.getBlob();
        } else {
            blob = new Blob([ str ]);
        }
        var f = new FileReader();
        f.onload = function(e) {
            callback(e.target.result)
        }
        f.readAsArrayBuffer(blob);
    }

    var fileName = "fileName.jpg";
    var contentType = "image/jpeg";
    if (file.type.toString().toLowerCase().indexOf("png") > -1) {
        fileName = "fileName.png";
        contentType = "image/png";
    }

    var xhrNativeObject = new XMLHttpRequest();
    xhrNativeObject.open("post", url, true);
    xhrNativeObject.setRequestHeader("Content-Type", contentType);

    xhrNativeObject.onload = function(event) {

        var targetResponse = event.currentTarget;
        if ((targetResponse.readyState == 4)
                && (targetResponse.status == 200)) {
            var obj = JSON.parse(targetResponse.responseText);
            console.log(obj.uploadImageId);
        } else {
            console.log("fail");
        }
    }

    var buffer = str2ab_blobreader(file, function(buf) {
        xhrNativeObject.send(buf);
    });
davidecavestro commented 10 years ago

About the request type, I was asking which value you got from println "request: "+request.class? About accessing a query param: you already did it declaring a param on the resource method and annotating it with @QueryParam (i.e. @QueryParam("cookie") String cookie)

confile commented 10 years ago

The result of println "request: "+request.class is

request: com.sun.proxy.$Proxy58

Do you have any idea how to fix this?

confile commented 10 years ago

@davidecavestro Do you have any idea how to fix this?

davidecavestro commented 10 years ago

The request instance is proxied with a jdk proxy, hence instanceof logic fails. That's one of the reasons why it should ben avoided using instanceof when possible. I'd proceed declaring a stream param as I suggested above then accessing the query params through the context

confile commented 10 years ago

@davidecavestro FormDataParam is not part of javax.ws.rs did you mean FormParam?

confile commented 10 years ago

@davidecavestro Which resources do I have to import to use FormDataParam?

davidecavestro commented 10 years ago

I meant FormDataParam available from jersey-multipart.

confile commented 9 years ago

I tried to use compile 'com.sun.jersey.contribs:jersey-multipart:1.18.2'

When I use this:

@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadMyFile(
    @FormDataParam('file') InputStream fileInputStream,
    @FormDataParam('file') FormDataContentDisposition contentDispositionHeader
    ...){...}

I get the following error:

2014-12-04 10:46:54,084 [localhost-startStop-1] ERROR [localhost].[/buddyis]  - Exception sending context initialized event to listener instance of class org.grails.jaxrs.web.JaxrsListener
Message: tried to access method com.sun.jersey.core.reflection.ReflectionHelper.getContextClassLoader()Ljava/lang/ClassLoader; from class com.sun.jersey.spi.scanning.AnnotationScannerListener
    Line | Method
->>   92 | <init>               in com.sun.jersey.spi.scanning.AnnotationScannerListener
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
|     59 | <init>               in com.sun.jersey.spi.scanning.PathProviderScannerListener
|     79 | init . . . . . . . . in com.sun.jersey.api.core.ScanningResourceConfig
|    104 | init                 in com.sun.jersey.api.core.PackagesResourceConfig
|     78 | <init> . . . . . . . in     ''
|     89 | <init>               in     ''
|    696 | createResourceConfig in com.sun.jersey.spi.container.servlet.WebComponent
|    674 | createResourceConfig in     ''
|    203 | init . . . . . . . . in     ''
|    374 | init                 in com.sun.jersey.spi.container.servlet.ServletContainer
|    557 | init . . . . . . . . in     ''
|     51 | init                 in org.grails.jaxrs.web.JerseyServlet
|    175 | init . . . . . . . . in org.grails.jaxrs.web.JaxrsContext
|    165 | init                 in     ''
|     45 | contextInitialized . in org.grails.jaxrs.web.JaxrsListener
|    262 | run                  in java.util.concurrent.FutureTask
|   1145 | runWorker . . . . .  in java.util.concurrent.ThreadPoolExecutor
|    615 | run                  in java.util.concurrent.ThreadPoolExecutor$Worker
^    745 | run . . . . . . . .  in java.lang.Thread
Error |
2014-12-04 10:46:54,111 [localhost-startStop-1] ERROR core.StandardContext  - Error listenerStart
Error |

Do you have any idea how to fix it?

prabhatsubedi commented 8 years ago

Hi @confile , did you find solution? I am stuck here too ...

confile commented 8 years ago

No