2

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();
    }
}
Johannes Dorn
  • 1,307
  • 1
  • 16
  • 34
  • You should include your code. – code11 Nov 17 '16 at 14:24
  • Since you don't get the file path, do you get the file ext with the file name. If so, folders don't have exts. So you should check to see if the file name has ".*". If the folder does not have .abc then you should consume the event. – SedJ601 Nov 17 '16 at 14:43
  • As described above, i have thought of that, but this is not a safe heuristic as folders are allowed to have dots in their name. Also, some files do not have a dot in them at all. My application is even likely to receive some of those. – Johannes Dorn Nov 17 '16 at 15:08

1 Answers1

1

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.

Johannes Dorn
  • 1,307
  • 1
  • 16
  • 34