Open onuridrisoglu opened 4 years ago
This is a good idea to offer a slightly better UX for uploading all files in a directory. Can be considered as progressive enhancement imo and ok to implement even if it won't work on all browsers (looks like it's well supported on desktop browsers except not on IE11).
Related resources:
One thing to consider is what should happen if the selected directory contains nested directories with more files (and possibly files of same name in sub directories)? I didn't check if the native APIs allow for getting the whole directory hierarchy or only some flattened list of files from the directory (or does it even get files from sub directories of selected directory?).
There could be an additional (optional) related feature that would automatically zip the directory on client side before uploading it. Or other special handling (or customizable handler method) might be needed.
We should probably investigate how it behaves in these cases with the native:
<input type="file" directory>
and try to mimic that behaviour. Possibly providing additional value/features.
Note, we can use webkitdirectory
attribute for uploading folders - see the Codepen illustrating how it works.
While it allows to select folders, it disallows selecting individual files, so you can't have both at the same time.
Here's the current version of the File and Directory Entries API: https://wicg.github.io/entries-api/
It contains some examples of how to get the directories from the drag and drop (which will require us to change how we handle event.dataTransfer
object) and helper functions e.g. getEntriesAsPromise()
and readFileEntry()
.
These APIs could be used to get the contents (individual files) from the folder uploaded using drag & drop.
Had some fun learning JS and injecting code into Vaadin client:
Problems: Does not pass folder structure to server, might be easiest to make with standard element.$server invocation
Test with text files, these will print to the screen
Tried only in Jetty, might be some problems in production release?
Use:
package fi.protieto.juuri.front.pages.map4;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.component.upload.Upload;
import com.vaadin.flow.component.upload.receivers.MultiFileMemoryBuffer;
import com.vaadin.flow.router.Route;
import org.apache.commons.io.IOUtils;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
// written on top of Vaadin autoupload example
@Route("upload-auto-upload-disabled")
public class AutoUpload extends Div {
// event listener explained
// https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
// handling folders explained
// https://stackoverflow.com/questions/3590058/does-html5-allow-drag-drop-upload-of-folders-or-a-folder-tree
// replacing functions explained:
// https://stackoverflow.com/questions/2136522/can-you-alter-a-javascript-function-after-declaring-it
// web component:
// https://github.com/vaadin/web-components/blob/main/packages/upload/src/vaadin-upload.js
// source in chrome:
// vaadin -> bundles -> node-modules -> @vaadin -> upload -> src -> vaadin-upload.js
public AutoUpload() {
MultiFileMemoryBuffer buffer = new MultiFileMemoryBuffer();
Upload upload = new Upload(buffer);
add(upload);
Button injectJS = new Button("inject");
injectJS.addClickListener(e -> {
upload.getElement().executeJs(
"" +
"function traverseFileTree(item, path, uploadThing) {" +
"if (item.isFile) {" +
"item.file(function(file) {" +
"uploadThing._addFile(file);" +
"});" +
"}" +
"if (item.isDirectory) {" +
"var dirReader = item.createReader();" +
"dirReader.readEntries(function(entries) {" +
"for (var i=0; i<entries.length; i++) {" +
"traverseFileTree(entries[i], path + item.name + '/', uploadThing);" +
"}" +
"});" +
"}" +
"}" +
"this.addEventListener('drop', function(event) {" +
"var items = event.dataTransfer.items;" +
"console.log('_onDrop top level item size: ' + items.length);" +
"for (var i=0; i<items.length; i++) {" +
"var item = items[i].webkitGetAsEntry();" +
"var length = traverseFileTree(item, '', this);" +
"}" +
"});" +
"this._addFiles = function(files) {" +
"if(!files.isArray) {" +
"console.log('_addFiles files as string (not array):' + JSON.stringify(files));" +
"return;" +
"}" +
"};"
);
});
add(injectJS);
upload.addSucceededListener(e -> {
try {
InputStream is = buffer.getInputStream(e.getFileName());
String result = IOUtils.toString(is, StandardCharsets.UTF_8);
System.out.println(e.getFileName() + ": " + result);
is.close();
}catch (Exception ex){
ex.printStackTrace();
}
});
}
}
It should be possible to select or drop a folder and upload the contents of the folder