diff --git a/packages/upload-core/upload.js b/packages/upload-core/upload.js index a8a1099fc..796b8ca23 100644 --- a/packages/upload-core/upload.js +++ b/packages/upload-core/upload.js @@ -24,12 +24,77 @@ class Upload { } this.file = file; - this.percentage = 0; this.options = Object.assign(options, defaults); this.completed = false; + this.percentage = 0; this.onError = []; this.onSuccess = []; this.onProgress = []; + this.bytesTotal = 0; + this.bytesSent = 0; + this.bytesScanned = 0; + this.timeoutID = undefined; + } + + inStatusCategory(status, category) { + return status >= category && status < category + 100; + } + + scan() { + const xhr = new window.XMLHttpRequest(); + + xhr.open('HEAD', this.upload.url, true); + xhr.setRequestHeader('Tus-Resumable', '1.0.0'); + xhr.setRequestHeader('X-Client-ID', this.options.clientId); + xhr.setRequestHeader('X-XSRF-TOKEN', this.getToken()); + + xhr.onload = () => { + if (!this.inStatusCategory(xhr.status, 200)) { + this.onError.forEach(cb => cb(xhr)); + return; + } + + this.bytesScanned = parseInt(xhr.getResponseHeader('AV-Scan-Bytes'), 10); + this.percentage = this.getPercentage(); + const result = xhr.getResponseHeader('AV-Scan-Result'); + + this.completed = true; + this.percentage = 100; + this.onSuccess.forEach(cb => cb()); + + if (result === 'accepted') { + this.completed = true; + this.percentage = 100; + const references = xhr.getResponseHeader('references'); + if (references) { + this.references = JSON.parse(references); + } + this.onSuccess.forEach(cb => cb()); + return; + } + + if (result === 'rejected') { + clearTimeout(this.timeoutId); + this.onError.forEach(cb => cb(new Error('File upload rejected'))); + return; + } + + this.timeoutId = setTimeout(() => { + this.scan(); + }, 50); + }; + + xhr.onerror = err => { + this.onError.forEach(cb => cb(err)); + }; + + xhr.send(null); + } + + getPercentage() { + const processedBytes = this.bytesSent + this.bytesScanned; + const combinedTotalBytes = this.bytesTotal * 2; + return processedBytes / combinedTotalBytes * 100; } getToken() { @@ -57,20 +122,36 @@ class Upload { this.onError.forEach(cb => cb(err)); }, onProgress: (bytesSent, bytesTotal) => { - this.percentage = bytesSent / bytesTotal * 100; this.bytesSent = bytesSent; this.bytesTotal = bytesTotal; - this.onProgress.forEach(cb => cb(bytesSent, bytesTotal)); + this.percentage = this.getPercentage(); + this.onProgress.forEach(cb => cb()); }, onSuccess: () => { const xhr = this.upload._xhr; // eslint-disable-line - this.percentage = 100; - this.completed = true; - const references = xhr.getResponseHeader('references'); - if (references) { - this.references = JSON.parse(references); + this.bytesScanned = + parseInt(xhr.getResponseHeader('AV-Scan-Bytes'), 10) || 0; + this.percentage = this.getPercentage(); + this.onProgress.forEach(cb => cb()); + + const result = xhr.getResponseHeader('AV-Scan-Result'); + if (result === 'accepted') { + this.completed = true; + this.percentage = 100; + const references = xhr.getResponseHeader('references'); + if (references) { + this.references = JSON.parse(references); + } + this.onSuccess.forEach(cb => cb()); + return; } - this.onSuccess.forEach(cb => cb()); + + if (result === 'rejected') { + this.onError.forEach(cb => cb(new Error('File upload rejected'))); + return; + } + + this.scan(); }, });