115 строки
3.6 KiB
TypeScript
115 строки
3.6 KiB
TypeScript
import { Aborter } from "./Aborter";
|
|
import { FileURL } from "./FileURL";
|
|
import { IUploadToAzureFileOptions } from "./highlevel.common";
|
|
import { Batch } from "./utils/Batch";
|
|
import {
|
|
FILE_RANGE_MAX_SIZE_BYTES,
|
|
DEFAULT_HIGH_LEVEL_PARALLELISM
|
|
} from "./utils/constants";
|
|
|
|
/**
|
|
* ONLY AVAILABLE IN BROWSERS.
|
|
*
|
|
* Uploads a browser Blob/File/ArrayBuffer/ArrayBufferView object to an Azure File.
|
|
*
|
|
* @export
|
|
* @param {Aborter} aborter Create a new Aborter instance with Aborter.none or Aborter.timeout(),
|
|
* goto documents of Aborter for more examples about request cancellation
|
|
* @param {Blob | ArrayBuffer | ArrayBufferView} browserData Blob, File, ArrayBuffer or ArrayBufferView
|
|
* @param {FileURL} fileURL
|
|
* @param {IUploadToAzureFileOptions} [options]
|
|
* @returns {Promise<void>}
|
|
*/
|
|
export async function uploadBrowserDataToAzureFile(
|
|
aborter: Aborter,
|
|
browserData: Blob | ArrayBuffer | ArrayBufferView,
|
|
fileURL: FileURL,
|
|
options?: IUploadToAzureFileOptions
|
|
): Promise<void> {
|
|
const browserBlob = new Blob([browserData]);
|
|
return UploadSeekableBlobToAzureFile(
|
|
aborter,
|
|
(offset: number, size: number): Blob => {
|
|
return browserBlob.slice(offset, offset + size);
|
|
},
|
|
browserBlob.size,
|
|
fileURL,
|
|
options
|
|
);
|
|
}
|
|
|
|
/**
|
|
* ONLY AVAILABLE IN BROWSERS.
|
|
*
|
|
* Uploads a browser Blob object to an Azure file. Requires a blobFactory as the data source,
|
|
* which need to return a Blob object with the offset and size provided.
|
|
*
|
|
* @param {Aborter} aborter Create a new Aborter instance with Aborter.none or Aborter.timeout(),
|
|
* goto documents of Aborter for more examples about request cancellation
|
|
* @param {(offset: number, size: number) => Blob} blobFactory
|
|
* @param {number} size
|
|
* @param {FileURL} fileURL
|
|
* @param {IUploadToAzureFileOptions} [options]
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async function UploadSeekableBlobToAzureFile(
|
|
aborter: Aborter,
|
|
blobFactory: (offset: number, size: number) => Blob,
|
|
size: number,
|
|
fileURL: FileURL,
|
|
options: IUploadToAzureFileOptions = {}
|
|
): Promise<void> {
|
|
if (!options.rangeSize) {
|
|
options.rangeSize = FILE_RANGE_MAX_SIZE_BYTES;
|
|
}
|
|
if (options.rangeSize < 0 || options.rangeSize > FILE_RANGE_MAX_SIZE_BYTES) {
|
|
throw new RangeError(
|
|
`options.rangeSize must be > 0 and <= ${FILE_RANGE_MAX_SIZE_BYTES}`
|
|
);
|
|
}
|
|
|
|
if (!options.fileHTTPHeaders) {
|
|
options.fileHTTPHeaders = {};
|
|
}
|
|
|
|
if (!options.parallelism) {
|
|
options.parallelism = DEFAULT_HIGH_LEVEL_PARALLELISM;
|
|
}
|
|
if (options.parallelism < 0) {
|
|
throw new RangeError(`options.parallelism cannot less than 0.`);
|
|
}
|
|
|
|
// Create the file
|
|
await fileURL.create(aborter, size, {
|
|
fileHTTPHeaders: options.fileHTTPHeaders,
|
|
metadata: options.metadata
|
|
});
|
|
|
|
const numBlocks: number = Math.floor((size - 1) / options.rangeSize) + 1;
|
|
let transferProgress: number = 0;
|
|
|
|
const batch = new Batch(options.parallelism);
|
|
for (let i = 0; i < numBlocks; i++) {
|
|
batch.addOperation(
|
|
async (): Promise<any> => {
|
|
const start = options.rangeSize! * i;
|
|
const end = i === numBlocks - 1 ? size : start + options.rangeSize!;
|
|
const contentLength = end - start;
|
|
await fileURL.uploadRange(
|
|
aborter,
|
|
blobFactory(start, contentLength),
|
|
start,
|
|
contentLength
|
|
);
|
|
// Update progress after block is successfully uploaded to server, in case of block trying
|
|
// TODO: Hook with convenience layer progress event in finer level
|
|
transferProgress += contentLength;
|
|
if (options.progress) {
|
|
options.progress({ loadedBytes: transferProgress });
|
|
}
|
|
}
|
|
);
|
|
}
|
|
return batch.do();
|
|
}
|