transloadit
Version:
Node.js SDK for Transloadit
94 lines • 3.9 kB
JavaScript
import { stat } from 'node:fs/promises';
import { basename } from 'node:path';
import debug from 'debug';
import pMap from 'p-map';
import { Upload } from 'tus-js-client';
const log = debug('transloadit');
export async function sendTusRequest({ streamsMap, assembly, requestedChunkSize, uploadConcurrency, onProgress, }) {
const streamLabels = Object.keys(streamsMap);
let totalBytes = 0;
let lastEmittedProgress = 0;
const sizes = {};
const haveUnknownLengthStreams = streamLabels.some((label) => !streamsMap[label]?.path);
// Initialize size data
await pMap(streamLabels, async (label) => {
const streamInfo = streamsMap[label];
if (!streamInfo) {
throw new Error(`Stream info not found for label: ${label}`);
}
const { path } = streamInfo;
if (path) {
const { size } = await stat(path);
sizes[label] = size;
totalBytes += size;
}
}, { concurrency: 5 });
const uploadProgresses = {};
async function uploadSingleStream(label) {
uploadProgresses[label] = 0;
const streamInfo = streamsMap[label];
if (!streamInfo) {
throw new Error(`Stream info not found for label: ${label}`);
}
const { stream, path } = streamInfo;
const size = sizes[label];
let chunkSize = requestedChunkSize;
let uploadLengthDeferred;
const isStreamLengthKnown = !!path;
if (!isStreamLengthKnown) {
// tus-js-client requires these options to be set for unknown size streams
// https://github.com/tus/tus-js-client/blob/master/docs/api.md#uploadlengthdeferred
uploadLengthDeferred = true;
if (chunkSize === Number.POSITIVE_INFINITY)
chunkSize = 50e6;
}
const onTusProgress = (bytesUploaded) => {
uploadProgresses[label] = bytesUploaded;
// get all uploaded bytes for all files
let uploadedBytes = 0;
for (const l of streamLabels) {
uploadedBytes += uploadProgresses[l] ?? 0;
}
// don't send redundant progress
if (lastEmittedProgress < uploadedBytes) {
lastEmittedProgress = uploadedBytes;
// If we have any unknown length streams, we cannot trust totalBytes
// totalBytes should then be undefined to mimic behavior of form uploads.
onProgress({
uploadedBytes,
totalBytes: haveUnknownLengthStreams ? undefined : totalBytes,
});
}
};
const filename = path ? basename(path) : label;
await new Promise((resolve, reject) => {
if (!assembly.assembly_ssl_url) {
reject(new Error('assembly_ssl_url is not present in the assembly status'));
return;
}
const tusOptions = {
endpoint: assembly.tus_url,
metadata: {
assembly_url: assembly.assembly_ssl_url,
fieldname: label,
filename,
},
onError: reject,
onProgress: onTusProgress,
onSuccess: resolve,
};
// tus-js-client doesn't like undefined/null
if (size != null)
tusOptions.uploadSize = size;
if (chunkSize)
tusOptions.chunkSize = chunkSize;
if (uploadLengthDeferred)
tusOptions.uploadLengthDeferred = uploadLengthDeferred;
const tusUpload = new Upload(stream, tusOptions);
tusUpload.start();
});
log(label, 'upload done');
}
await pMap(streamLabels, uploadSingleStream, { concurrency: uploadConcurrency });
}
//# sourceMappingURL=tus.js.map