ente/src/services/upload/multiPartUploadService.ts

104 lines
3.1 KiB
TypeScript
Raw Normal View History

2021-08-11 07:20:26 +00:00
import { CHUNKS_COMBINED_FOR_A_UPLOAD_PART, DataStream } from './uploadService';
2021-08-09 15:15:11 +00:00
import NetworkClient from './networkClient';
import * as convert from 'xml-js';
2021-08-11 07:20:26 +00:00
import UIService, { RANDOM_PERCENTAGE_PROGRESS_FOR_PUT } from './uiService';
2021-08-09 15:15:11 +00:00
interface PartEtag {
PartNumber: number;
2021-08-11 09:39:05 +00:00
ETag: string;
2021-08-09 15:15:11 +00:00
}
2021-08-11 07:20:26 +00:00
export interface MultipartUploadURLs {
objectKey: string;
partURLs: string[];
completeURL: string;
}
2021-08-10 05:36:03 +00:00
export function calculatePartCount(chunkCount: number) {
const partCount = Math.ceil(chunkCount / CHUNKS_COMBINED_FOR_A_UPLOAD_PART);
2021-08-09 15:15:11 +00:00
return partCount;
}
export async function uploadStreamUsingMultipart(
filename: string,
2021-08-13 03:38:02 +00:00
dataStream: DataStream
) {
2021-08-10 05:36:03 +00:00
const uploadPartCount = calculatePartCount(dataStream.chunkCount);
const multipartUploadURLs = await NetworkClient.fetchMultipartUploadURLs(
2021-08-13 03:38:02 +00:00
uploadPartCount
2021-08-09 15:15:11 +00:00
);
const fileObjectKey = await uploadStreamInParts(
2021-08-10 05:36:03 +00:00
multipartUploadURLs,
dataStream.stream,
2021-08-09 15:15:11 +00:00
filename,
2021-08-13 03:38:02 +00:00
uploadPartCount
2021-08-09 15:15:11 +00:00
);
return fileObjectKey;
}
export async function uploadStreamInParts(
multipartUploadURLs: MultipartUploadURLs,
2021-08-10 05:36:03 +00:00
dataStream: ReadableStream<Uint8Array>,
2021-08-09 15:15:11 +00:00
filename: string,
2021-08-13 03:38:02 +00:00
uploadPartCount: number
2021-08-09 15:15:11 +00:00
) {
2021-08-10 05:36:03 +00:00
const streamReader = dataStream.getReader();
2021-08-09 15:15:11 +00:00
const percentPerPart = getRandomProgressPerPartUpload(uploadPartCount);
const partEtags: PartEtag[] = [];
2021-08-09 15:15:11 +00:00
for (const [
index,
fileUploadURL,
] of multipartUploadURLs.partURLs.entries()) {
2021-08-10 05:36:03 +00:00
const uploadChunk = await combineChunksToFormUploadPart(streamReader);
2021-08-11 05:26:40 +00:00
const progressTracker = UIService.trackUploadProgress(
filename,
percentPerPart,
2021-08-13 03:38:02 +00:00
index
);
const eTag = await NetworkClient.putFilePart(
fileUploadURL,
uploadChunk,
2021-08-13 03:38:02 +00:00
progressTracker
);
2021-08-11 09:39:05 +00:00
partEtags.push({ PartNumber: index + 1, ETag: eTag });
2021-08-09 15:15:11 +00:00
}
await completeMultipartUpload(partEtags, multipartUploadURLs.completeURL);
return multipartUploadURLs.objectKey;
}
export function getRandomProgressPerPartUpload(uploadPartCount: number) {
2021-08-09 15:15:11 +00:00
const percentPerPart = Math.round(
2021-08-13 03:38:02 +00:00
RANDOM_PERCENTAGE_PROGRESS_FOR_PUT() / uploadPartCount
2021-08-09 15:15:11 +00:00
);
return percentPerPart;
}
export async function combineChunksToFormUploadPart(
2021-08-13 03:38:02 +00:00
streamReader: ReadableStreamDefaultReader<Uint8Array>
) {
2021-08-09 15:15:11 +00:00
const combinedChunks = [];
for (let i = 0; i < CHUNKS_COMBINED_FOR_A_UPLOAD_PART; i++) {
const { done, value: chunk } = await streamReader.read();
2021-08-09 15:15:11 +00:00
if (done) {
break;
}
for (let index = 0; index < chunk.length; index++) {
combinedChunks.push(chunk[index]);
}
}
return Uint8Array.from(combinedChunks);
}
async function completeMultipartUpload(
partEtags: PartEtag[],
2021-08-13 03:38:02 +00:00
completeURL: string
) {
2021-08-09 15:15:11 +00:00
const options = { compact: true, ignoreComment: true, spaces: 4 };
const body = convert.js2xml(
{ CompleteMultipartUpload: { Part: partEtags } },
2021-08-13 03:38:02 +00:00
options
2021-08-09 15:15:11 +00:00
);
await NetworkClient.completeMultipartUpload(completeURL, body);
}