28
* A helper to read a file from a provider with open/read/close capability into a stream.
29
*/
30
>
export async function readFileIntoStream<T>(
io.ts
31
>
provider: IFileSystemProviderWithOpenReadWriteCloseCapability,
32
>
resource: URI,
33
>
target: WriteableStream<T>,
34
>
transformer: IDataTransformer<VSBuffer, T>,
35
>
options: ICreateReadStreamOptions,
36
>
token: CancellationToken
37
>
): Promise<void> {
38
>
let error: Error | undefined = undefined;
39
>
40
>
try {
41
>
await doReadFileIntoStream(provider, resource, target, transformer, options, token);
42
>
} catch (err) {
43
error = err;
45
>
if (error && options.errorTransformer) {
46
error = options.errorTransformer(error);
47
}
49
>
if (typeof error !== 'undefined') {
50
target.error(error);
51
}
53
>
target.end();
54
>
}
55
>
}
56
57
>
async function doReadFileIntoStream<T>(provider: IFileSystemProviderWithOpenReadWriteCloseCapability, resource: URI, target: WriteableStream<T>, transformer: IDataTransformer<VSBuffer, T>, options: ICreateReadStreamOptions, token: CancellationToken): Promise<void> {
io.ts
58
>
59
>
// Check for cancellation
60
>
throwIfCancelled(token);
61
>
62
>
// open handle through provider
63
>
const handle = await provider.open(resource, { create: false });
64
>
65
>
try {
66
>
67
>
// Check for cancellation
68
>
throwIfCancelled(token);
69
>
70
>
let totalBytesRead = 0;
71
>
let bytesRead = 0;
72
>
let allowedRemainingBytes = (options && typeof options.length === 'number') ? options.length : undefined;
73
>
74
>
let buffer = VSBuffer.alloc(Math.min(options.bufferSize, typeof allowedRemainingBytes === 'number' ? allowedRemainingBytes : options.bufferSize));
75
>
76
>
let posInFile = options && typeof options.position === 'number' ? options.position : 0;
77
>
let posInBuffer = 0;
78
>
do {
79
>
// read from source (handle) at current position (pos) into buffer (buffer) at
80
>
// buffer position (posInBuffer) up to the size of the buffer (buffer.byteLength).
81
>
bytesRead = await provider.read(handle, posInFile, buffer.buffer, posInBuffer, buffer.byteLength - posInBuffer);
82
>
83
>
posInFile += bytesRead;
84
>
posInBuffer += bytesRead;
85
>
totalBytesRead += bytesRead;
86
>
87
>
if (typeof allowedRemainingBytes === 'number') {
88
allowedRemainingBytes -= bytesRead;
89
}
91
>
// when buffer full, create a new one and emit it through stream
92
>
if (posInBuffer === buffer.byteLength) {
93
>
await target.write(transformer(buffer));
94
>
95
>
buffer = VSBuffer.alloc(Math.min(options.bufferSize, typeof allowedRemainingBytes === 'number' ? allowedRemainingBytes : options.bufferSize));
96
>
97
>
posInBuffer = 0;
98
>
}
99
>
} while (bytesRead > 0 && (typeof allowedRemainingBytes !== 'number' || allowedRemainingBytes > 0) && throwIfCancelled(token) && throwIfTooLarge(totalBytesRead, options));
100
>
101
>
// wrap up with last buffer (also respect maxBytes if provided)
102
>
if (posInBuffer > 0) {
103
>
let lastChunkLength = posInBuffer;
104
>
if (typeof allowedRemainingBytes === 'number') {
105
lastChunkLength = Math.min(posInBuffer, allowedRemainingBytes);
106
}
108
>
target.write(transformer(buffer.slice(0, lastChunkLength)));
109
>
}
110
>
} catch (error) {
111
throw ensureFileSystemProviderError(error);
113
>
await provider.close(handle);
114
>
}
115
>
}
116
117
>
function throwIfCancelled(token: CancellationToken): boolean {
io.ts
118
>
if (token.isCancellationRequested) {
119
throw canceled();
120
}
122
>
return true;
123
>
}
124
125
>
function throwIfTooLarge(totalBytesRead: number, options: ICreateReadStreamOptions): boolean {
io.ts
126
>
127
>
// Return early if file is too large to load and we have configured limits
128
>
if (typeof options?.limits?.size === 'number' && totalBytesRead > options.limits.size) {
129
throw createFileSystemProviderError(localize('fileTooLargeError', "File is too large to open"), FileSystemProviderErrorCode.FileTooLarge);
130
}
132
>
return true;
133
>
}