Skip to content

Commit

Permalink
feat: create read stream copy in case of file descriptor
Browse files Browse the repository at this point in the history
  • Loading branch information
trivikr committed Feb 21, 2022
1 parent e166117 commit 84cae7b
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 15 deletions.
46 changes: 46 additions & 0 deletions packages/hash-stream-node/src/fsCreateReadStream.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import * as fs from "fs";

import { fsCreateReadStream } from "./fsCreateReadStream";

jest.setTimeout(30000);

describe(fsCreateReadStream.name, () => {
const mockFileContents = fs.readFileSync(__filename, "utf8");

it("uses file descriptor if defined", (done) => {
fs.promises.open(__filename, "r").then((fd) => {
if ((fd as any).createReadStream) {
const readStream = (fd as any).createReadStream();
const readStreamCopy = fsCreateReadStream(readStream);

const chunks: Array<Buffer> = [];
readStreamCopy.on("data", (chunk) => {
chunks.push(chunk);
});
readStreamCopy.on("end", () => {
const outputFileContents = Buffer.concat(chunks).toString();
expect(outputFileContents).toEqual(mockFileContents);
done();
});
} else {
console.log(`Skipping createReadStream test as it's not available.`);
done();
}
});
});

it("uses start and end if file descriptor is not defined", (done) => {
const readStream = fs.createReadStream(__filename);
const readStreamCopy = fsCreateReadStream(readStream);

const chunks: Array<Buffer> = [];
readStreamCopy.on("data", (chunk) => {
chunks.push(chunk);
});
readStreamCopy.on("end", () => {
const outputFileContents = Buffer.concat(chunks).toString();
expect(outputFileContents).toEqual(mockFileContents);
done();
});
});
});
8 changes: 8 additions & 0 deletions packages/hash-stream-node/src/fsCreateReadStream.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { createReadStream, ReadStream } from "fs";

export const fsCreateReadStream = (readStream: ReadStream) =>
createReadStream(readStream.path, {
fd: (readStream as any).fd,
start: (readStream as any).start,
end: (readStream as any).end,
});
3 changes: 1 addition & 2 deletions packages/hash-stream-node/src/isFileStream.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,4 @@ import { Readable } from "stream";
export const isFileStream = (stream: Readable): stream is ReadStream =>
typeof (stream as ReadStream).path === "string" ||
Buffer.isBuffer((stream as ReadStream).path) ||
// @ts-expect-error fd is not defined in @types/node
typeof (stream as ReadStream).fd === "number";
typeof (stream as any).fd === "number";
9 changes: 4 additions & 5 deletions packages/hash-stream-node/src/readableStreamHasher.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import { Hash } from "@aws-sdk/types";
import { createReadStream } from "fs";
import { Readable, Writable } from "stream";

import { fsCreateReadStream } from "./fsCreateReadStream";
import { HashCalculator } from "./HashCalculator";
import { isFileStream } from "./isFileStream";
import { readableStreamHasher } from "./readableStreamHasher";

jest.mock("./fsCreateReadStream");
jest.mock("./HashCalculator");
jest.mock("./isFileStream");
jest.mock("fs");
Expand Down Expand Up @@ -51,7 +53,7 @@ describe(readableStreamHasher.name, () => {
});

it("creates a copy in case of fileStream", () => {
(createReadStream as jest.Mock).mockReturnValue(
(fsCreateReadStream as jest.Mock).mockReturnValue(
new Readable({
read: (size) => {},
})
Expand All @@ -62,10 +64,7 @@ describe(readableStreamHasher.name, () => {
readableStreamHasher(mockHashCtor, fsReadStream);

expect(isFileStream).toHaveBeenCalledWith(fsReadStream);
expect(createReadStream).toHaveBeenCalledWith(fsReadStream.path, {
start: (fsReadStream as any).start,
end: (fsReadStream as any).end,
});
expect(fsCreateReadStream).toHaveBeenCalledWith(fsReadStream);
});

it("computes hash for a readable stream", async () => {
Expand Down
10 changes: 2 additions & 8 deletions packages/hash-stream-node/src/readableStreamHasher.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,13 @@
import { HashConstructor, StreamHasher } from "@aws-sdk/types";
import { createReadStream } from "fs";
import { Readable } from "stream";

import { fsCreateReadStream } from "./fsCreateReadStream";
import { HashCalculator } from "./HashCalculator";
import { isFileStream } from "./isFileStream";

export const readableStreamHasher: StreamHasher<Readable> = (hashCtor: HashConstructor, readableStream: Readable) => {
// ToDo: throw if readableStream is already flowing and it's copy can't be created.
// ToDo: create accurate copy if filestream is created from file descriptor.
const streamToPipe = isFileStream(readableStream)
? createReadStream(readableStream.path, {
start: (readableStream as any).start,
end: (readableStream as any).end,
})
: readableStream;
const streamToPipe = isFileStream(readableStream) ? fsCreateReadStream(readableStream) : readableStream;

const hash = new hashCtor();
const hashCalculator = new HashCalculator(hash);
Expand Down

0 comments on commit 84cae7b

Please sign in to comment.