From: Piotr Kosko
Date: Mon, 16 Nov 2020 08:33:10 +0000 (+0100)
Subject: [Filetransfer] Fixed performance of strict Blob object passing
X-Git-Tag: submit/tizen/20201116.125215^2
X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=e6250f7b56a12370ca9b93576d14979fc16c118e;p=platform%2Fcore%2Fapi%2Fcordova-plugins.git
[Filetransfer] Fixed performance of strict Blob object passing
[Bug] Because of need to pass strict Blob object to FormData since
Chromium M76, there is a need to increase a performance to pass
cordova tests - finish test in timeout.
Because of that, xml request creation was moved before reading a file
to make possible to trigger abort callback, even when the file is not
fully loaded to memory yet.
[Verification] Below code (based on failing test) produces times below 600ms.
E.g. TEST FINISHED: Time taken: 555
/// , you need to have running http server accepting POST
/// requests running on SERVER ip address
var SERVER = "http://192.168.0.220:5000";
var persistentRoot;
var GRACE_TIME_DELTA = 600; // in milliseconds
var DEFAULT_FILESYSTEM_SIZE = 1024 * 50; //filesystem size in bytes
var ABORT_DELAY = 100; // for abort() tests
window.requestFileSystem(LocalFileSystem.PERSISTENT, DEFAULT_FILESYSTEM_SIZE,
function (fileSystem) {
persistentRoot = fileSystem.root;
runTest();
},
function () {
throw new Error('Failed to initialize persistent file system.');
}
);
var unexpectedCallbacks = {
httpFail: function () { },
httpWin: function () { },
fileSystemFail: function () { },
fileSystemWin: function () { },
fileOperationFail: function () { },
fileOperationWin: function () { },
};
var writeFile = function (fileSystem, name, content, success) {
fileSystem.getFile(name, { create: true },
function (fileEntry) {
fileEntry.createWriter(function (writer) {
writer.onwrite = function () {
success(fileEntry);
};
writer.onabort = function (evt) {
throw new Error('aborted creating test file \'' + name + '\': ' + evt);
};
writer.error = function (evt) {
throw new Error('aborted creating test file \'' + name + '\': ' + evt);
};
if (cordova.platformId === 'browser') {
// var builder = new BlobBuilder();
// builder.append(content + '\n');
var blob = new Blob([content + '\n'], { type: 'text/plain' });
writer.write(blob);
} else {
writer.write(content + "\n");
}
}, unexpectedCallbacks.fileOperationFail);
},
function () {
throw new Error('could not create test file \'' + name + '\'');
}
);
};
var runTest = function () {
transfer = new FileTransfer();
// assign onprogress handler
var defaultOnProgressHandler = function (event) {
if (event.lengthComputable) {
expect(event.loaded).toBeGreaterThan(1);
expect(event.total).toBeGreaterThan(0);
expect(event.total).not.toBeLessThan(event.loaded);
expect(event.lengthComputable).toBe(true, 'lengthComputable');
} else {
// In IE, when lengthComputable === false, event.total somehow is equal to 2^64
if (isIE) {
expect(event.total).toBe(Math.pow(2, 64));
} else {
expect(event.total).toBe(0);
}
}
};
transfer.onprogress = defaultOnProgressHandler;
root = persistentRoot;
fileName = 'testFile.txt';
localFilePath = root.toURL() + fileName;
uploadParams = {};
uploadParams.value1 = "test";
uploadParams.value2 = "param";
uploadOptions = new FileUploadOptions();
uploadOptions.fileKey = "file";
uploadOptions.fileName = fileName;
uploadOptions.mimeType = "text/plain";
uploadOptions.params = uploadParams;
var fileURL = SERVER + '';
var startTime;
var uploadFail = function (e) {
console.log('uploadFail - ' + JSON.stringify(e));
console.log('TEST FINISHED: Time taken: ' + (new Date() - startTime));
};
var fileWin = function () {
console.log('fileWin');
startTime = +new Date();
transfer.onprogress = (s) => {
console.log('upload on progress: ' + JSON.stringify(s) +
'time taken: ' + (new Date() - startTime)) };
console.log('Call upload, time taken: ' + (new Date() - startTime))
transfer.upload(localFilePath, fileURL, (s) => {
console.log('uploadWin!!! ' + JSON.stringify(s)); }, uploadFail, uploadOptions, true);
console.log('After upload call, time taken: ' + (new Date() - startTime))
setTimeout(function () {
console.log('Call abort, time taken: ' + (new Date() - startTime))
transfer.abort();
}, ABORT_DELAY);
};
writeFile(root, fileName, new Array(100000).join('aborttest!'), fileWin);
}
Change-Id: I29717417009dc309456158529cebc85b904e31d3
---
diff --git a/src/lib/plugins/cordova-plugin-file-transfer/tizen/FileTransfer.js b/src/lib/plugins/cordova-plugin-file-transfer/tizen/FileTransfer.js
index 2042474..bcf20cf 100755
--- a/src/lib/plugins/cordova-plugin-file-transfer/tizen/FileTransfer.js
+++ b/src/lib/plugins/cordova-plugin-file-transfer/tizen/FileTransfer.js
@@ -98,86 +98,95 @@ exports = {
function successCB(entry) {
if (entry.isFile) {
- var fullPath = entry.toURL();
- entry.file(function(file) {
- function uploadFile(blobFile) {
- var fd = new FormData();
+ // initialize XMLHTTPRequest (without starting it)
+ var xhr = uploads[id] = new XMLHttpRequest();
- fd.append(fileKey, blobFile, fileName);
+ xhr.open(httpMethod, server);
- for (var prop in params) {
- if(params.hasOwnProperty(prop)) {
- fd.append(prop, params[prop]);
- }
- }
- var xhr = uploads[id] = new XMLHttpRequest();
+ // Fill XHR headers
+ for (var header in headers) {
+ if (headers.hasOwnProperty(header)) {
+ xhr.setRequestHeader(header, headers[header]);
+ }
+ }
+
+ xhr.onload = function(evt) {
+
+ if (xhr.status === 200) {
+ uploads[id] && delete uploads[id];
+ successCallback({
+ bytesSent: file.size,
+ responseCode: xhr.status,
+ response: xhr.response
+ });
+ } else if (xhr.status === 404) {
+ fail(FileTransferError.INVALID_URL_ERR, this.status, this.response);
+ } else {
+ fail(FileTransferError.CONNECTION_ERR, this.status, this.response);
+ }
+ };
- xhr.open(httpMethod, server);
+ xhr.ontimeout = function(evt) {
+ fail(FileTransferError.CONNECTION_ERR, this.status, this.response);
+ };
- // Fill XHR headers
- for (var header in headers) {
- if (headers.hasOwnProperty(header)) {
- xhr.setRequestHeader(header, headers[header]);
- }
- }
+ xhr.onerror = function() {
+ fail(FileTransferError.CONNECTION_ERR, this.status, this.response);
+ };
- xhr.onload = function(evt) {
- if (xhr.status === 200) {
- uploads[id] && delete uploads[id];
- successCallback({
- bytesSent: file.size,
- responseCode: xhr.status,
- response: xhr.response
- });
- } else if (xhr.status === 404) {
- fail(FileTransferError.INVALID_URL_ERR, this.status, this.response);
- } else {
- fail(FileTransferError.CONNECTION_ERR, this.status, this.response);
- }
- };
-
- xhr.ontimeout = function(evt) {
- fail(FileTransferError.CONNECTION_ERR, this.status, this.response);
- };
-
- xhr.onerror = function() {
- fail(FileTransferError.CONNECTION_ERR, this.status, this.response);
- };
-
- xhr.onabort = function () {
- fail(FileTransferError.ABORT_ERR, this.status, this.response);
- };
-
- xhr.upload.onprogress = function (e) {
- successCallback(e);
- };
-
- xhr.send(fd);
-
- // Special case when transfer already aborted, but XHR isn't sent.
- // In this case XHR won't fire an abort event, so we need to check if transfers record
- // isn't deleted by filetransfer.abort and if so, call XHR's abort method again
- if (!uploads[id]) {
- xhr.abort();
- }
+ xhr.onabort = function () {
+ fail(FileTransferError.ABORT_ERR, this.status, this.response);
+ };
+
+ xhr.upload.onprogress = function (e) {
+ successCallback(e);
+ };
+ // end of XMLHTTPRequest initialization
+
+ var fullPath = entry.toURL();
+ var fileHandle;
+
+ function closeHandle() {
+ if (fileHandle) {
+ fileHandle.close();
}
+ }
- var fileHandle;
- try {
- fileHandle = tizen.filesystem.openFile(fullPath, 'r');
- var fileBlob = fileHandle.readBlob();
- uploadFile(fileBlob);
- } catch (e) {
- fail(FileTransferError.ABORT_ERR, 'Could not read file');
- } finally {
- if (fileHandle) {
- fileHandle.close();
+ function uploadFile(blobFile) {
+ closeHandle();
+ // create FormData
+ var fd = new FormData();
+ fd.append(fileKey, blobFile, fileName);
+ for (var prop in params) {
+ if(params.hasOwnProperty(prop)) {
+ fd.append(prop, params[prop]);
}
}
- }, function(error) {
- fail(FileTransferError.CONNECTION_ERR);
- });
+ // sending already initialized request
+ xhr.send(fd);
+
+ // Special case when transfer already aborted, but XHR isn't sent.
+ // In this case XHR won't fire an abort event, so we need to check if transfers record
+ // isn't deleted by filetransfer.abort and if so, call XHR's abort method again
+ if (!uploads[id]) {
+ xhr.abort();
+ }
+ }
+
+ try {
+ fileHandle = tizen.filesystem.openFile(fullPath, 'r');
+ var fileBlob = fileHandle.readBlobNonBlocking(
+ uploadFile,
+ function(error) {
+ closeHandle();
+ fail(FileTransferError.ABORT_ERR, 'Could not read file ' + e);
+ }
+ );
+ } catch (e) {
+ closeHandle();
+ fail(FileTransferError.ABORT_ERR, 'Could not read file ' + e);
+ }
}
}