Обратный звонок из сервлета в GWT при загрузке файла в динамической форме SmartGWT без использования источника данных - PullRequest
2 голосов
/ 13 декабря 2011

Я использую динамическую форму SmartGWT submitForm () для реализации функции загрузки файлов на стороне клиента, вот код:

final String DEFAULT_FILE_UPLOAD_SERVICE_PATH = "upload";
final String TARGET = "uploadTarget";

VLayout body = new VLayout();

uploadForm = new DynamicForm();

 // initialise the hidden frame
NamedFrame frame = new NamedFrame(TARGET);
frame.setWidth("1px");
frame.setHeight("1px");
frame.setVisible(false);

uploadForm.setEncoding(Encoding.MULTIPART);
uploadForm.setMethod(FormMethod.POST);
// set the (hidden) form target
uploadForm.setTarget(TARGET);

uploadForm.setAction(DEFAULT_FILE_UPLOAD_SERVICE_PATH);

// initialise the File name field
uploadItem = new UploadItem("filename");
uploadItem.setName("filename");
uploadItem.setTitle("File name");

// set the fields into the form
uploadForm.setFields(uploadItem);

// add the Upload Form and the (hidden) Frame to the main layout container
body.addMember(uploadForm);
body.addMember(frame);

А на стороне сервера у меня есть сервлет для обработки файлазапрос на загрузку с использованием библиотеки загрузки файлов Apache, вот код:

@Singleton
@SuppressWarnings("serial")
public class FileUploadServlet extends HttpServlet {
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        this.process(request, response);
    }

    @Override
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        this.process(request, response);
    }

    private void process(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // check that we have a file upload request
        if (ServletFileUpload.isMultipartContent(request)) {
            processFiles(request, response);
        }
    }

    private File tmpDir;
    private static final String DESTINATION_DIR_PATH = "/files/upload";
    private File destinationDir;

    public void init(ServletConfig config) throws ServletException {
        super.init(config);

        tmpDir = new File(((File) getServletContext().getAttribute("javax.servlet.context.tempdir")).toString());

        if (!tmpDir.isDirectory()) {
          throw new ServletException(tmpDir.toString() + " is not a directory");
        }

        Log.debug("tmpDir: " + tmpDir.toString());

        String realPath = getServletContext().getRealPath(DESTINATION_DIR_PATH);
        destinationDir = new File(realPath);

        if (!destinationDir.isDirectory()) {
          throw new ServletException(DESTINATION_DIR_PATH + " is not a directory");
        }
    }

    private void processFiles(HttpServletRequest request, HttpServletResponse response) 
            throws ServletException, IOException {

        // create a factory for disk-based file items
        DiskFileItemFactory factory = new DiskFileItemFactory();

        // set the size threshold, above which content will be stored on disk
        factory.setSizeThreshold(1 * 1024 * 1024); // 1 MB

        // set the temporary directory (this is where files that exceed the threshold will be stored)
        factory.setRepository(tmpDir);

        // create a new file upload handler
        ServletFileUpload upload = new ServletFileUpload(factory);

        try {
            // parse the request
            List<?> items = upload.parseRequest(request);

            // process the uploaded items
            Iterator<?> itr = items.iterator();

            while (itr.hasNext()) {
                FileItem item = (FileItem) itr.next();

                // write the uploaded file to the application's file staging area
                File file = new File(destinationDir, item.getName());
                item.write(file);
            }

        } catch (FileUploadException e) {
            Log.error("Error encountered while parsing the request", e);
        } catch (Exception e) {
            Log.error("Error encountered while uploading file", e);
        }
    }
}

При использовании вышеуказанного кода загрузка файлов работает нормально.Мой вопрос в том, что я не знаю, как перезвонить сервлету.Один учебник говорит об использовании скрытой рамки, но я не знаю, как это сделать.Может ли кто-нибудь помочь мне с этим?Спасибо.

Ответы [ 3 ]

1 голос
/ 14 декабря 2011

Вам нужно записать тег как ответ вашего сервера, содержащий код JavaScript, который будет выполняться внутри фрейма.Этот код должен будет перемещаться вне фрейма для вызова некоторой функции, которую вы поместили на главную страницу (например, top.someFunction ()).Чтобы настроить функцию на главной странице, используйте JSNI, чтобы прикрепить функцию JavaScript к окну ($ wnd в JSNI).

Обратите внимание, что вам не нужно ничего делать, если вы используете SmartGWTPro + - это решение только для людей, использующих версию LGPL.

0 голосов
/ 19 апреля 2017

Я знаю, что это старый вопрос, но я бы хотел улучшить ответ @Charles Kendrick с помощью примера кода.

Документация SmartGWT объясняет различные способы достижения этого, мызаинтересован в этой части

Фоновая загрузка без сервера Smart GWT

Достичь фоновой загрузки файлов без использования сервера Smart GWT также возможно, хотя и значительно более продвинуто.В дополнение к описанным выше шагам, создает скрытый элемент на странице и использует DynamicForm.target , чтобы нацелить отправку формы в этом IFRAME.Чтобы получил уведомление об обратном вызове, когда загрузка завершится , после обработки загрузки файла ваш сервер должен вывести HTML-контент для IFRAME, включающий блок , который будетвыйдите из IFRAME (как правило, через глобальную «вершину» JavaScript) и вызовите глобальный метод , который вы объявили в качестве обратного вызова.

На стороне клиента

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();
            }
        });

, чтобы это работало, нам нужно зарегистрировать метод обратного вызова, используя JSNI, давайте сделаем это

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();
}

, наконец, на стороне сервера мы должны вернуть блок скрипта ввызвать функцию обратного вызова.В этой реализации используется Apache CXF, но важной частью является возврат тега script для вызова функции обратного вызова

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;
    }
}
0 голосов
/ 06 июня 2012

Мне удалось получить обратный вызов от моего сервлета альтернативным способом без использования JNSI. Я также использую IFRAME в качестве цели формы, чтобы она не открывала новую вкладку.

В основном это включает в себя таблицу в базе данных для хранения статуса моего действия. Я использую таймер, который запрашивает мою таблицу из асинхронного вызова.

Со стороны сервлета он изменяет статус записи, созданной после завершения задания.

//My service creates an entry in my progressbar database
DelaiServiceAsync.Util.getInstance().launchProgressBar(
new DefaultAutoErrorHandlingAsyncCallback<Long>() {

    @Override
    public void onSuccess(Long pResult) {
    //gets the id from the entry created
    final Long token = pResult;

    //I give the token to the servlet so it can update the status of the entry
    tokenHItem.setDefaultValue(token.toString());
    tokenHItem.setValue(token.toString());
    importForm.rememberValues();

    // here you can show a prompt asking the user to wait. SC.showPrompt( ... );

    //form submitted to the servlet
    importForm.submitForm();

    //Timer setup
    //timeout after 5 tick
    final int size = 5;

    Timer timer = new Timer() {

        private int counter = 0;
        private String status = "started";
        @Override
        public void run() {

        // asynchronous call querying the table expecting a change of status
        DelaiServiceAsync.Util.getInstance().getProgress(token,new DefaultAutoErrorHandlingAsyncCallback<ProgressDTO>() {

            @Override
            public void onSuccess(ProgressDTO pResult) {
                status = pResult.getStatus();
            }

            @Override
            public void onFailure(Throwable caught) {

                super.onFailure(caught);
                Log.debug(fonctionFormatee
                      + "::getProgress - Failure "
                      + caught.getMessage());
            }
            });

        if (counter == size) {
            //Timeout
            cancel();
            // we can clear the prompt here. SC.clearPrompt();
            return;
        }

        //check the status from my response
        //Here it says that my file has been written succesfully servlet side
        if ("written".equals(status)) {
            cancel();
            // we can clear the prompt here. SC.clearPrompt();
            //we leave the timer
            return;

        }
        else if ("error".equals(status)) {
            //Finished
            cancel();
            // we can clear the prompt here. SC.clearPrompt();
            SC.error("error");
            return;
        }

        counter++;
        }

    };
    //delay before the timer starts
    timer.schedule(10);
    //time interval between timer ticks
    timer.scheduleRepeating(10);

    }

    @Override
    public void onFailure(Throwable caught) {

    super.onFailure(caught);
    Log.debug(fonctionFormatee + "::launchProgressBar - Failure "
          + caught.getMessage());
     // we can clear the prompt here. SC.clearPrompt();
    }
});
...