UNPKG

@microsoft/microsoft-graph-client

Version:
121 lines 5.91 kB
import { __awaiter } from "tslib"; import { GraphClientError } from "../../../GraphClientError"; /** * @class * FileObject class for Readable Stream upload */ export class StreamUpload { constructor(content, name, size) { this.content = content; this.name = name; this.size = size; if (!content || !name || !size) { throw new GraphClientError("Please provide the Readable Stream content, name of the file and size of the file"); } } /** * @public * Slices the file content to the given range * @param {Range} range - The range value * @returns The sliced file part */ sliceFile(range) { return __awaiter(this, void 0, void 0, function* () { let rangeSize = range.maxValue - range.minValue + 1; /* readable.readable Is true if it is safe to call readable.read(), * which means the stream has not been destroyed or emitted 'error' or 'end' */ const bufs = []; /** * The sliceFile reads the first `rangeSize` number of bytes from the stream. * The previousSlice property is used to seek the range of bytes in the previous slice. * Suppose, the sliceFile reads bytes from `10 - 20` from the stream but the upload of this slice fails. * When the user resumes, the stream will have bytes from position 21. * The previousSlice.Range is used to compare if the requested range is cached in the previousSlice property or present in the Readable Stream. */ if (this.previousSlice) { if (range.minValue < this.previousSlice.range.minValue) { throw new GraphClientError("An error occurred while uploading the stream. Please restart the stream upload from the first byte of the file."); } if (range.minValue < this.previousSlice.range.maxValue) { const previousRangeMin = this.previousSlice.range.minValue; const previousRangeMax = this.previousSlice.range.maxValue; // Check if the requested range is same as previously sliced range if (range.minValue === previousRangeMin && range.maxValue === previousRangeMax) { return this.previousSlice.fileSlice; } /** * The following check considers a possibility * of an upload failing after some of the bytes of the previous slice * were successfully uploaded. * Example - Previous slice range - `10 - 20`. Current requested range is `15 - 20`. */ if (range.maxValue === previousRangeMax) { return this.previousSlice.fileSlice.slice(range.minValue, range.maxValue + 1); } /** * If an upload fails after some of the bytes of the previous slice * were successfully uploaded and the new Range.Maximum is greater than the previous Range.Maximum * Example - Previous slice range - `10 - 20`. Current requested range is `15 - 25`, * then read the bytes from position 15 to 20 from previousSlice.fileSlice and read bytes from position 21 to 25 from the Readable Stream */ bufs.push(this.previousSlice.fileSlice.slice(range.minValue, previousRangeMax + 1)); rangeSize = range.maxValue - previousRangeMax; } } if (this.content && this.content.readable) { if (this.content.readableLength >= rangeSize) { bufs.push(this.content.read(rangeSize)); } else { bufs.push(yield this.readNBytesFromStream(rangeSize)); } } else { throw new GraphClientError("Stream is not readable."); } const slicedChunk = Buffer.concat(bufs); this.previousSlice = { fileSlice: slicedChunk, range }; return slicedChunk; }); } /** * @private * Reads the specified byte size from the stream * @param {number} size - The size of bytes to be read * @returns Buffer with the given length of data. */ readNBytesFromStream(size) { return new Promise((resolve, reject) => { const chunks = []; let remainder = size; let length = 0; this.content.on("end", () => { if (remainder > 0) { return reject(new GraphClientError("Stream ended before reading required range size")); } }); this.content.on("readable", () => { /** * (chunk = this.content.read(size)) can return null if size of stream is less than 'size' parameter. * Read the remainder number of bytes from the stream iteratively as they are available. */ let chunk; while (length < size && (chunk = this.content.read(remainder)) !== null) { length += chunk.length; chunks.push(chunk); if (remainder > 0) { remainder = size - length; } } if (length === size) { return resolve(Buffer.concat(chunks)); } if (!this.content || !this.content.readable) { return reject(new GraphClientError("Error encountered while reading the stream during the upload")); } }); }); } } //# sourceMappingURL=StreamUpload.js.map