Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ added.
* `.fileRetry(file, chunk)` Something went wrong during upload of a specific file, uploading is being
retried.
* `.fileError(file, message, chunk)` An error occurred during upload of a specific file.
* `.readErrors(files, folders, event)` This event fires before fileAdded or filesAdded events only if errors occur while reading files or folders. First argument `files` is collection of files read errors, second argument `folders` is collection of folder read errors.
* `.uploadStart()` Upload has been started on the Flow object.
* `.complete()` Uploading completed.
* `.progress()` Uploading progress.
Expand Down
200 changes: 148 additions & 52 deletions src/flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,55 +230,109 @@
*/
webkitReadDataTransfer: function (event) {
var $ = this;
var queue = event.dataTransfer.items.length;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's unlikely such a huge diff (+149 −52) could be efficiently reviewed. Could you provide a minimum testcase (in the test folder) and a less intrusive bugfix?

var files = [];
each(event.dataTransfer.items, function (item) {
var entry = item.webkitGetAsEntry();
if (!entry) {
decrement();
return ;
getEntries(event.dataTransfer.items).then(function (result) {
getFiles(result.files).then(function (entries) {
var files = [];
var errors = [];
each(entries, function (entry) {
if (entry.error) {
errors.push(entry);
} else {
files.push(entry);
}
});
if (result.errors.length || errors.length) {
$.fire('readErrors', errors, result.errors, event);
}
if (files.length) {
$.addFiles(files, event);
}
});
});
function getEntries(items) {
var files = [];
var errors = [];
var promises = [];

function readEntry(entry, promises) {
if (entry.isFile) {
files.push(entry);
} else if (entry.isDirectory) {
promises.push(readDirectory(entry));
}
}
if (entry.isFile) {
// due to a bug in Chrome's File System API impl - #149735
fileReadSuccess(item.getAsFile(), entry.fullPath);
} else {
readDirectory(entry.createReader());

function readDirectory(entry) {
var reader = entry.createReader();
return new Promise(function (resolve, reject) {
var promises = [];
readEntries(entry, reader, promises, resolve);
});
}
});
function readDirectory(reader) {
reader.readEntries(function (entries) {
if (entries.length) {
queue += entries.length;
each(entries, function(entry) {
if (entry.isFile) {
var fullPath = entry.fullPath;
entry.file(function (file) {
fileReadSuccess(file, fullPath);
}, readError);
} else if (entry.isDirectory) {
readDirectory(entry.createReader());
}

function readEntries(entry, reader, promises, resolve) {
reader.readEntries(function (entries) {
if (entries.length) {
var promises2 = [];
each(entries, function (entry2) {
readEntry(entry2, promises2);
});
promises.push(Promise.all(promises2));
readEntries(entry, reader, promises, resolve);
return;
}
resolve(Promise.all(promises));
}, function (error) {
errors.push({
path: entry.fullPath,
error: error
});
readDirectory(reader);
} else {
decrement();
resolve(promises);
});
}

each(items, function (item) {
var entry = item.webkitGetAsEntry();
if (!entry) {
return;
}
if (entry.isFile) {
// due to a bug in Chrome's File System API impl - #149735
files.push(getFile(item.getAsFile(), entry.fullPath));
return;
}
}, readError);
readEntry(entry, promises);
});

return new Promise(function (resolve, reject) {
return Promise.all(promises).then(function () {
resolve({ files: files, errors: errors });
});
});
}
function getFiles(entries) {
return Promise.all(entries.map(function (entry) {
return new Promise(function (resolve, reject) {
if (entry.file) {
var fullPath = entry.fullPath;
entry.file(function (file) {
resolve(getFile(file, fullPath));
}, function (file) {
resolve({
path: entry.fullPath,
error: file
});
});
} else {
resolve(entry);
}
});
}));
}
function fileReadSuccess(file, fullPath) {
function getFile(file, fullPath) {
// relative path should not start with "/"
file.relativePath = fullPath.substring(1);
files.push(file);
decrement();
}
function readError(fileError) {
decrement();
throw fileError;
}
function decrement() {
if (--queue == 0) {
$.addFiles(files, event);
}
return file;
}
},

Expand Down Expand Up @@ -588,8 +642,12 @@
* @param {Event} [event] event is optional
*/
addFiles: function (fileList, event) {
var $ = this;
var files = [];
each(fileList, function (file) {
var errors = [];
var promises = [];

function addFile(file) {
// https://github.com/flowjs/flow.js/issues/55
if ((!ie10plus || ie10plus && file.size > 0) && !(file.size % 4096 === 0 && (file.name === '.' || file.fileName === '.'))) {
var uniqueIdentifier = this.generateUniqueIdentifier(file);
Expand All @@ -600,16 +658,54 @@
}
}
}
}, this);
if (this.fire('filesAdded', files, event)) {
each(files, function (file) {
if (this.opts.singleFile && this.files.length > 0) {
this.removeFile(this.files[0]);
}
this.files.push(file);
}, this);
this.fire('filesSubmitted', files, event);
}

/**
* Chrome's FileSystem API has a bug that files from dropped folders or files from input dialog's selected folder,
* with read errors (has absolute paths which exceed 260 chars) will have zero file size.
*/
function validateFile(file) {
// files with size greater than zero can upload
if (file.size > 0) {
addFile.bind($)(file);
return;
}

// try to read from from zero size file,
// if error occurs than file cannot be uploaded
promises.push(new Promise(function (resolve, reject) {
var reader = new FileReader();
reader.onloadend = function () {
if (reader.error) {
errors.push({
path: file.webkitRelativePath || file.name,
error: reader.error
});
} else {
addFile.bind($)(file);
}
resolve();
}.bind($);
reader.readAsArrayBuffer(file);
}));
}

each(fileList, validateFile);

Promise.all(promises).then(function () {
if (errors.length) {
this.fire('readErrors', errors, [], event);
}
if (this.fire('filesAdded', files, event)) {
each(files, function (file) {
if (this.opts.singleFile && this.files.length > 0) {
this.removeFile(this.files[0]);
}
this.files.push(file);
}, this);
this.fire('filesSubmitted', files, event);
}
}.bind(this));
},


Expand Down