Johannes Dorn
Johannes Dorn

Reputation: 1327

How to handle drag & drop upload of folders?

I am implementing an upload functionality in my Vaadin web application that uses drag & drop, like in this Vaadin sampler example.

My code is almost identical to the example code there. When trying my code and the linked example however, there is one important difference: When dropping a folder, my code starts to show the progress spinner, but never finishes. None of the StreamVariable callback methods are ever called. No bytes are ever actually uploaded. The sample application simply does nothing (user visible at least).

I actually don't even want the user to be able to upload folders, but i need to be able to detect whether the dropped "file" is a folder.

Unfortunately, neither the WrapperTransferable nor the Html5File objects i get give me any indication. I've only come up with heuristics to determine whether i have a file or folder: Check that the MIME type is empty, check that the filesisze is small, check that there are no dots in the filename. None of these is very safe though.

How can i detect and prevent an attempted upload of a folder?

Here is my code (again, almost a straight copy of the linked Vaadin example).

    public class AttachmentDropBox extends DragAndDropWrapper implements DropHandler {

    private final ProgressBar progress;

    public AttachmentDropBox(Component root, ProgressBar progress) {
        super(root);
        this.progress = progress;
        progress.setIndeterminate(true);
        setDropHandler(this);
    }

    @Override
    public void drop(DragAndDropEvent dropEvent) {
        // expecting this to be an html5 drag
        if (!(dropEvent.getTransferable() instanceof WrapperTransferable)) {
            return;
        }

        final WrapperTransferable tr = (WrapperTransferable) dropEvent.getTransferable();
        final Html5File[] files = tr.getFiles();
        if (files == null) {
            return;
        }

        if (files.length != 1) {
            Notification.show("Please upload single files only", Notification.Type.WARNING_MESSAGE);
            return;
        }

        Html5File file = files[0];

        if (file.getFileSize() > settings.getClientMaxAttachmentSize()) {
            Notification.show(
                    MessageFormat.format("File rejected. Attachments may not exceed {0} in size.",
                            FileUtils.byteCountToDisplaySize(settings.getClientMaxAttachmentSize())),
                    Notification.Type.WARNING_MESSAGE);
            return;
        }

        final ByteArrayOutputStream bas = new ByteArrayOutputStream();
        final StreamVariable streamVariable = new StreamVariable() {

            @Override
            public OutputStream getOutputStream() {
                return bas;
            }

            @Override
            public boolean listenProgress() {
                return false;
            }

            @Override
            public void onProgress(final StreamingProgressEvent event) {
                // not called
            }

            @Override
            public void streamingStarted(final StreamingStartEvent event) {
                LOG.info("Upload started: " + event.getFileName());
            }

            @Override
            public void streamingFinished(final StreamingEndEvent event) {
                progress.setVisible(false);
                LOG.info("Upload finished: " + event.getFileName());
            }

            @Override
            public void streamingFailed(final StreamingErrorEvent event) {
                Notification.show(
                        MessageFormat.format("Failed to upload file: {0}",
                                StringUtils.abbreviate(event.getException().getMessage(), 100)),
                        Notification.Type.ERROR_MESSAGE);
                LOG.error("Failed to upload file '{}'", event.getFileName(), event.getException());
                progress.setVisible(false);
            }

            @Override
            public boolean isInterrupted() {
                return false;
            }
        };
        file.setStreamVariable(streamVariable);

        progress.setVisible(true);
    }

    @Override
    public AcceptCriterion getAcceptCriterion() {
        return AcceptAll.get();
    }
}

Upvotes: 2

Views: 481

Answers (1)

Johannes Dorn
Johannes Dorn

Reputation: 1327

This problem has been fixed in Vaadin 7.7.0

This is the corresponding change note:

Folder upload is not supported by most of the browsers and can cause StreamVariable methods not fire on some configurations. This fix tries to detect and prevent uploading of folders.

Upvotes: 1

Related Questions