SmartGWT – Upload file with Apache CXF REST service

abril 19, 2017

It has been a long time since I updated an app which we have developed with SmartGWT. This time we needed to upload a file to update some catalog.

The SmartGWT documentation explains different ways to achieve this, we’re interested in this part

Background upload without the Smart GWT Server

Achieving background file upload without using the Smart GWT server is also possible although considerably more advanced. In addition to the steps above, create a hidden ** element in the page, and **use DynamicForm.target to target the form submission at this IFRAME. In order receive a callback notification when the upload completes, after processing the file upload, your server should output HTML content for the IFRAME that includes a block which will navigate out of the IFRAME (generally via the JavaScript global “top”) and call a global method you have declared as a callback.

So, the first thing we are going to need is the server side service to receive such file. This app had REST web services with Apache CXF already working, so we should use this same framework (credit goes to javatips for this).

import org.apache.cxf.jaxrs.ext.multipart.Attachment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.activation.DataHandler;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.util.List;

@Path("catalogos")
public class CatalogosREST {
    private final Logger log = LoggerFactory.getLogger(CatalogosREST.class);

    private String getFileName(MultivaluedMap<String, String> header) {
        String[] contentDisposition = header.getFirst("Content-Disposition").split(";");
        for (String filename : contentDisposition) {
            if ((filename.trim().startsWith("filename"))) {
                String[] name = filename.split("=");
                String exactFileName = name[1].trim().replaceAll("\"", "");
                return exactFileName;
            }
        }
        return "unknown";
    }

    @POST
    @Path("/upload")
    @Consumes("multipart/form-data")
    public Response uploadFile(List<Attachment> attachments, @Context HttpServletRequest request) {
        log.debug("se ha recibido una peticion para subir un archivo de proveedores [attachments: {}, request: {}]", attachments, request);
        Response response = null;
        for (Attachment attachment : attachments) {
            DataHandler handler = attachment.getDataHandler();
            try {
                InputStream stream = handler.getInputStream();
                MultivaluedMap<String, String> map = attachment.getHeaders();
                log.debug("headers: {}", map);
                log.debug("fileName: {}", getFileName(map));
                OutputStream out = new FileOutputStream(new File("/tmp/" + getFileName(map)));

                int read = 0;
                byte[] bytes = new byte[1024];
                while ((read = stream.read(bytes)) != -1) {
                    out.write(bytes, 0, read);
                }
                stream.close();
                out.flush();
                out.close();
                StringBuilder scriptOnFileUploadFinished = new StringBuilder(
                        "<script type=\"application/javascript\">\n" +
                                "      window.top.onFileUploadFinished();\n" +
                                "    </script>"
                );
                response = Response.ok(scriptOnFileUploadFinished.toString()).build();
            } catch (Exception e) {
                String error = "Ocurrio un error al subir el archivo de proveedores";
                log.error(error, e);
                response = Response.serverError().entity(error).build();
            }
        }
        return response;
    }
}

You can test this REST web service with something like postman.

It’s worth noting that we are returning a block of script tag from this web service. This is necessary to notify our client code that the file upload has finished.

Now, on the client side let’s make our code as per the docs.

final DynamicForm frmUpload = new DynamicForm();
        NamedFrame iframeUpload = new NamedFrame("iframeUpload");
        iframeUpload.setVisible(false);
        frmUpload.setTarget(iframeUpload.getName());
        frmUpload.setEncoding(Encoding.MULTIPART);
        frmUpload.setMethod(FormMethod.POST);
        UploadItem itmUpload = new UploadItem("itmProveedores", "Archivo Proveedores");
        frmUpload.setItems(itmUpload);
        frmUpload.setAction(GWT.getHostPageBaseURL() + "api/catalogos/upload");
        IButton btnUpload = new IButton("Subir archivo proveedorees");
        btnUpload.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent clickEvent) {
                frmUpload.submitForm();
            }
        });

for this to work, we need to register the callback method using JSNI, let’s do that

private static void onFileUploadFinished(){
        SC.say("upload finished");
    }

    private native void registerOnFileUploadFinished()/*-{
        $wnd.onFileUploadFinished = @com.sample.CatalogosForm::onFileUploadFinished();
    }-*/;

...
/*on the constructor, load event or main entry point -it depends on your app-*/
public CatalogosForm(){
    /*initial setup*/
    registerOnFileUploadFinished();
}

and that’s it, your File should upload successfully and display a message when the upload is finished.

sources:
https://www.smartclient.com/smartgwt/javadoc/com/smartgwt/client/docs/Upload.html
http://www.javatips.net/blog/cxf-rest-file-upload?page=2
http://stackoverflow.com/questions/8488078/get-call-back-from-servlet-in-gwt-when-upload-file-in-smartgwts-dynamic-form-wi
https://webtoolkit.googleblog.com/2008/07/getting-to-really-know-gwt-part-1-jsni.html
http://stackoverflow.com/questions/2161388/calling-a-parent-window-function-from-an-iframe

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: