🐛 Fix race condition in file download
This commit is contained in:
parent
662c2d5f43
commit
8b1ad6f7e2
3 changed files with 11 additions and 13 deletions
|
@ -15,9 +15,11 @@ export default function saveResponse(
|
||||||
//Remember file size
|
//Remember file size
|
||||||
const headerLength = Number(response.headers['content-length']);
|
const headerLength = Number(response.headers['content-length']);
|
||||||
|
|
||||||
|
const writeStream = fs.createWriteStream(filePath);
|
||||||
|
|
||||||
//If we receive a part of the response, write it to disk
|
//If we receive a part of the response, write it to disk
|
||||||
let previousChunk: Promise<void> = new Promise((innerResolve) => { innerResolve(); });
|
|
||||||
let totalLength = 0;
|
let totalLength = 0;
|
||||||
|
const chunkPromises: Array<Promise<void>> = [];
|
||||||
response.on('data', (chunk: Buffer) => {
|
response.on('data', (chunk: Buffer) => {
|
||||||
totalLength += chunk.length;
|
totalLength += chunk.length;
|
||||||
|
|
||||||
|
@ -26,18 +28,12 @@ export default function saveResponse(
|
||||||
return reject(`Expected length ${headerLength} but received at least ${totalLength}.`);
|
return reject(`Expected length ${headerLength} but received at least ${totalLength}.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
//If previous chunk was not yet written to disk, wait until it finished to avoid a race condition
|
|
||||||
previousChunk.then(() => {
|
|
||||||
previousChunk = new Promise((innerResolve, innerReject) => {
|
|
||||||
//Write chunk to disk
|
//Write chunk to disk
|
||||||
fs.appendFile(filePath, chunk, { encoding: 'binary' }, (error) => {
|
chunkPromises.push(new Promise((writeResolve) => {
|
||||||
if (error) {
|
writeStream.write(chunk, () => {
|
||||||
return reject(`Could not write to disk: [${error.code}] ${error.name}: ${error.message}.`);
|
writeResolve();
|
||||||
}
|
|
||||||
innerResolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
//If we finished reading response, check for correctness, then return it
|
//If we finished reading response, check for correctness, then return it
|
||||||
|
@ -50,7 +46,7 @@ export default function saveResponse(
|
||||||
//wait until everything is written to disk, then return file name
|
//wait until everything is written to disk, then return file name
|
||||||
//TODO: need to automatically delete file once it is no longer used
|
//TODO: need to automatically delete file once it is no longer used
|
||||||
//TODO: need to provide methods to seek through file
|
//TODO: need to provide methods to seek through file
|
||||||
previousChunk.then(() => {
|
Promise.all(chunkPromises).then(() => {
|
||||||
resolve(filePath);
|
resolve(filePath);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -24,6 +24,7 @@ export default function extractFileStream(file: ISsnFileEntry, inputStream: stre
|
||||||
//pipe into decompression
|
//pipe into decompression
|
||||||
const decompressTransform = zlib.createInflateRaw();
|
const decompressTransform = zlib.createInflateRaw();
|
||||||
decompressTransform.on('error', (error) => {
|
decompressTransform.on('error', (error) => {
|
||||||
|
//TODO: need to throw error sync, not async
|
||||||
throw new Error(`Error during decompression: ${error.message}`);
|
throw new Error(`Error during decompression: ${error.message}`);
|
||||||
});
|
});
|
||||||
curStream = curStream.pipe(decompressTransform);
|
curStream = curStream.pipe(decompressTransform);
|
||||||
|
|
|
@ -32,6 +32,7 @@ export default async function getPatch(product: Product, from: number, to: numbe
|
||||||
//Then we need to wait for disks to finish download before we can extract individual files
|
//Then we need to wait for disks to finish download before we can extract individual files
|
||||||
//TODO: we can optimize this to already extract some files as soon as their relevant parts are downloaded
|
//TODO: we can optimize this to already extract some files as soon as their relevant parts are downloaded
|
||||||
const diskFilenames = await Promise.all(diskFiles);
|
const diskFilenames = await Promise.all(diskFiles);
|
||||||
|
console.debug(diskFilenames);
|
||||||
//const dvArray = bufferArray.map((buffer) => new DataView(buffer));
|
//const dvArray = bufferArray.map((buffer) => new DataView(buffer));
|
||||||
|
|
||||||
//TODO: Verify that downloaded files match the hash in `solidpkg.pieces`
|
//TODO: Verify that downloaded files match the hash in `solidpkg.pieces`
|
||||||
|
|
Loading…
Reference in a new issue