TUS: Detect and disallow concurrent uploads (#339)
This commit is contained in:
commit
13cbbc8342
4 changed files with 38 additions and 6 deletions
|
@ -1303,6 +1303,7 @@
|
||||||
"Retrying...": "Retrying...",
|
"Retrying...": "Retrying...",
|
||||||
"Uploading...": "Uploading...",
|
"Uploading...": "Uploading...",
|
||||||
"Creating...": "Creating...",
|
"Creating...": "Creating...",
|
||||||
|
"Stopped. Duplicate session detected.": "Stopped. Duplicate session detected.",
|
||||||
"Use a URL": "Use a URL",
|
"Use a URL": "Use a URL",
|
||||||
"Edit Cover Image": "Edit Cover Image",
|
"Edit Cover Image": "Edit Cover Image",
|
||||||
"Cover Image": "Cover Image",
|
"Cover Image": "Cover Image",
|
||||||
|
|
|
@ -66,6 +66,8 @@ export default function WebUploadItem(props: Props) {
|
||||||
return __('Retrying...');
|
return __('Retrying...');
|
||||||
case 'error':
|
case 'error':
|
||||||
return __('Failed.');
|
return __('Failed.');
|
||||||
|
case 'conflict':
|
||||||
|
return __('Stopped. Duplicate session detected.');
|
||||||
default:
|
default:
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
@ -130,7 +132,7 @@ export default function WebUploadItem(props: Props) {
|
||||||
<div className="claim-upload__progress--label">lbry://{params.name}</div>
|
<div className="claim-upload__progress--label">lbry://{params.name}</div>
|
||||||
<div className={'claim-upload__progress--outer card--inline'}>
|
<div className={'claim-upload__progress--outer card--inline'}>
|
||||||
<div className={'claim-upload__progress--inner'} style={{ width: `${progress}%` }}>
|
<div className={'claim-upload__progress--inner'} style={{ width: `${progress}%` }}>
|
||||||
{resolveProgressStr()}
|
<span className="claim-upload__progress--inner-text">{resolveProgressStr()}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -375,6 +375,15 @@
|
||||||
.claim-upload__progress--inner {
|
.claim-upload__progress--inner {
|
||||||
background: var(--color-primary-alt);
|
background: var(--color-primary-alt);
|
||||||
padding: var(--spacing-xxs);
|
padding: var(--spacing-xxs);
|
||||||
|
height: 2.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.claim-upload__progress--inner-text {
|
||||||
|
position: absolute;
|
||||||
|
width: 80%;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -48,8 +48,18 @@ export function makeResumableUploadRequest(
|
||||||
id: new Date().getTime(),
|
id: new Date().getTime(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const urlOptions = {};
|
||||||
|
if (params.uploadUrl) {
|
||||||
|
// Resuming from previous upload. TUS clears the resume fingerprint on any
|
||||||
|
// 4xx error, so we need to use the fixed URL mode instead.
|
||||||
|
urlOptions.uploadUrl = params.uploadUrl;
|
||||||
|
} else {
|
||||||
|
// New upload, so use `endpoint`.
|
||||||
|
urlOptions.endpoint = RESUMABLE_ENDPOINT;
|
||||||
|
}
|
||||||
|
|
||||||
const uploader = new tus.Upload(file, {
|
const uploader = new tus.Upload(file, {
|
||||||
endpoint: RESUMABLE_ENDPOINT,
|
...urlOptions,
|
||||||
chunkSize: UPLOAD_CHUNK_SIZE_BYTE,
|
chunkSize: UPLOAD_CHUNK_SIZE_BYTE,
|
||||||
retryDelays: [0, 5000, 10000, 15000],
|
retryDelays: [0, 5000, 10000, 15000],
|
||||||
parallelUploads: 1,
|
parallelUploads: 1,
|
||||||
|
@ -62,11 +72,21 @@ export function makeResumableUploadRequest(
|
||||||
onShouldRetry: (err, retryAttempt, options) => {
|
onShouldRetry: (err, retryAttempt, options) => {
|
||||||
window.store.dispatch(doUpdateUploadProgress({ params, status: 'retry' }));
|
window.store.dispatch(doUpdateUploadProgress({ params, status: 'retry' }));
|
||||||
const status = err.originalResponse ? err.originalResponse.getStatus() : 0;
|
const status = err.originalResponse ? err.originalResponse.getStatus() : 0;
|
||||||
return !inStatusCategory(status, 400) || status === STATUS_CONFLICT || status === STATUS_LOCKED;
|
return !inStatusCategory(status, 400);
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (err) => {
|
||||||
window.store.dispatch(doUpdateUploadProgress({ params, status: 'error' }));
|
const status = err.originalResponse ? err.originalResponse.getStatus() : 0;
|
||||||
reject(new Error(error));
|
if (status === STATUS_CONFLICT || status === STATUS_LOCKED) {
|
||||||
|
window.store.dispatch(doUpdateUploadProgress({ params, status: 'conflict' }));
|
||||||
|
reject(
|
||||||
|
new Error(
|
||||||
|
`${status}: concurrent upload detected. Uploading the same file from multiple tabs or windows is not allowed.`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
window.store.dispatch(doUpdateUploadProgress({ params, status: 'error' }));
|
||||||
|
reject(new Error(err));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onProgress: (bytesUploaded, bytesTotal) => {
|
onProgress: (bytesUploaded, bytesTotal) => {
|
||||||
const percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2);
|
const percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2);
|
||||||
|
|
Loading…
Reference in a new issue