Skip to content

Commit

Permalink
renamed getStream to getResponse + bump version
Browse files Browse the repository at this point in the history
  • Loading branch information
good-lly committed Jul 2, 2024
1 parent e825352 commit aee59e1
Show file tree
Hide file tree
Showing 10 changed files with 69 additions and 42 deletions.
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,11 @@ const data = await s3.get('path/to/life.txt');
console.log(data);

// get a stream of a large file (first chunk)
const firstChunk = await s3.getStream('path/to/large-file.mp4', false, 0);
const firstChunk = await s3.getResponse('path/to/large-file.mp4', false, 0).body;

// get a stream of a large file (all chunks)
const allChunks = await s3.getStream('path/to/large-file.mp4', true);
for await (const chunk of allChunks) {
const allChunks = await s3.getResponse('path/to/large-file.mp4', true);
for await (const chunk of allChunks.body) {
console.log(chunk);
}

Expand Down Expand Up @@ -201,16 +201,16 @@ Not tested, but should work with other S3 compatible services. Full list - soon
- **Behavior**: Retrieves an object from the bucket.
- **Returns**: Promise<string\>: A promise that resolves to the content of the object.

**getStream(key: string, wholeFile?: boolean, rangeFrom?: number, rangeTo?: number, opts?: Object): Promise<ReadableStream | null\>**
**getResponse(key: string, wholeFile?: boolean, rangeFrom?: number, rangeTo?: number, opts?: Object): Promise<Response\>**

- **Input**:
- `key: string`: The key of the object to get.
- `wholeFile?: boolean` (optional): Whether to get the whole file or a part (default: true).
- `rangeFrom?: number` (optional): The byte range from to get if not getting the whole file (default: 0).
- `rangeTo?: number` (optional): The byte range to to get if not getting the whole file (default: maxRequestSizeInBytes).
- `rangeTo?: number` (optional): The byte range to to get if not getting the whole file (default: maxRequestSizeInBytes). Note: rangeTo is inclusive.
- `opts?: Object` (optional): Additional options for the get operation.
- **Behavior**: Retrieves a readable stream of an object from the bucket.
- **Returns**: Promise<ReadableStream | null>: A promise that resolves to a ReadableStream of the object content.
- **Behavior**: Retrieves a response of an object from the bucket.
- **Returns**: Promise<Response\>: A promise that resolves to a Response of the object content. Use readableStream() to get the stream from .body.

**delete(key: string): Promise<string>**

Expand Down
30 changes: 24 additions & 6 deletions dev/node-dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,10 +173,28 @@ app.get('get/:dir/:key', async c => {
});

app.get('/get-stream/:key', async c => {
const s3 = new S3(configCFS3);
const key = c.req.param('key');
const resp = await s3.getStream(key, false, 0, 7 * 1024 * 1024);
return c.text(resp);
try {
console.time();
const s3 = new S3(configCFS3);
const key = c.req.param('key');
const resp = await s3.getResponse(key, false, 0, 7 * 1024 * 1024);
// const chunks = [];
// for await (let chunk of resp) {
// chunks.push(chunk);
// }
// const buf = Buffer.concat(chunks);
// const bufLength = Buffer.byteLength(buf);

console.log('resp::: lenght ', parseInt(resp.readableLength, 10));
console.timeEnd();
return c.text(resp);
} catch (error) {
console.error('Errorrrrr:', error);
if (error.toString().indexOf('status 404: Unknown - Not Found') > -1) {
return c.json({ error: '404 Not Found' });
}
return c.json({ error: 'Failed to get stream' });
}
});

app.get('list/:prefix', async c => {
Expand All @@ -198,8 +216,8 @@ app.get('/', async c => {
const s3list = await s3.list();
// const collected = [];

// const jsonStream = s3.getStream(s3list[0].key).body;
// await (await s3.getStream(s3list[0].key)).pipeThrough(new JSONParseStream('$.*')).pipeTo(
// const jsonStream = s3.getResponse(s3list[0].key).body;
// await (await s3.getResponse(s3list[0].key)).pipeThrough(new JSONParseStream('$.*')).pipeTo(
// new WritableStream({
// write(obj) {
// collected.push(obj);
Expand Down
8 changes: 4 additions & 4 deletions lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,15 +170,15 @@ declare class S3 {
*/
get(key: string, opts?: Record<string, any>): Promise<string>;
/**
* Get a stream of an object from the bucket.
* Get a response of an object from the bucket.
* @param {string} key - The key of the object to get.
* @param {boolean} [wholeFile=true] - Whether to get the whole file or a part.
* @param {number} [rangeFrom=0] - The range from to get if not getting the whole file.
* @param {number} [rangeTo=this.maxRequestSizeInBytes] - The range to to get if not getting the whole file.
* @param {number} [rangeTo=this.maxRequestSizeInBytes] - The range to to get if not getting the whole file. Note: rangeTo is inclusive.
* @param {Object} [opts={}] - Additional options for the get operation.
* @returns {Promise<ReadableStream>} A readable stream of the object content.
* @returns {Promise<Response>} Response of the object content. Use readableStream() to get the stream from .body.
*/
getStream(key: string, wholeFile?: boolean, rangeFrom?: number, rangeTo?: number, opts?: Record<string, any>): Promise<ReadableStream | null>;
getResponse(key: string, wholeFile?: boolean, rangeFrom?: number, rangeTo?: number, opts?: Record<string, any>): Promise<Response>;
/**
* Put an object into the bucket.
* @param {string} key - The key of the object to put. To create a folder, include a trailing slash.
Expand Down
13 changes: 6 additions & 7 deletions lib/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/index.js.map

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions lib/index.min.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions lib/index.min.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ultralight-s3",
"version": "0.0.2",
"version": "0.0.3",
"description": "🪽 A turbo lightweight S3 client, no-dependency, ideal for edges or platforms like @cloudflare @aws @Azure @GoogleCloudPlatform @ceph @minio",
"main": "./lib/index.min",
"module": "./lib/index.min.js",
Expand Down
15 changes: 7 additions & 8 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -616,33 +616,32 @@ class S3 {
}

/**
* Get a stream of an object from the bucket.
* Get a response of an object from the bucket.
* @param {string} key - The key of the object to get.
* @param {boolean} [wholeFile=true] - Whether to get the whole file or a part.
* @param {number} [rangeFrom=0] - The range from to get if not getting the whole file.
* @param {number} [rangeTo=this.maxRequestSizeInBytes] - The range to to get if not getting the whole file.
* @param {number} [rangeTo=this.maxRequestSizeInBytes] - The range to to get if not getting the whole file. Note: rangeTo is inclusive.
* @param {Object} [opts={}] - Additional options for the get operation.
* @returns {Promise<ReadableStream>} A readable stream of the object content.
* @returns {Promise<Response>} Response of the object content. Use readableStream() to get the stream from .body.
*/
async getStream(
async getResponse(
key: string,
wholeFile: boolean = true,
rangeFrom: number = 0,
rangeTo: number = this.maxRequestSizeInBytes,
opts: Record<string, any> = {},
): Promise<ReadableStream | null> {
): Promise<Response> {
const query = opts;
const headers = {
[HEADER_CONTENT_TYPE]: JSON_CONTENT_TYPE,
[HEADER_AMZ_CONTENT_SHA256]: UNSIGNED_PAYLOAD,
...(wholeFile ? {} : { range: `bytes=${rangeFrom}-${rangeTo}` }),
...(wholeFile ? {} : { range: `bytes=${rangeFrom}-${rangeTo - 1}` }),
};
const encodedKey = uriResourceEscape(key);
const { url, headers: signedHeaders } = await this._sign('GET', encodedKey, query, headers, '');
const urlWithQuery = `${url}?${new URLSearchParams(query)}`;

const res = await this._sendRequest(urlWithQuery, 'GET', signedHeaders);
return res.body;
return this._sendRequest(urlWithQuery, 'GET', signedHeaders);
}

/**
Expand Down
17 changes: 14 additions & 3 deletions tests/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -211,17 +211,28 @@ describe('S3 class', () => {
const content = 'This is a test for streaming';
await s3.put(key, content);

const s3stream = await s3.getStream(key);
expect(s3stream).toBeTruthy();
const s3stream = await s3.getResponse(key);
expect(s3stream.body).toBeTruthy();

let streamContent = [];
for await (const chunk of s3stream) {
for await (const chunk of s3stream.body) {
// convert Buffer bytes to string
streamContent.push(Buffer.from(chunk));
}
const streamContentString = Buffer.concat(streamContent).toString('utf-8');
expect(streamContentString).toBe(content);

// try ranged request
const s3streamRange = await s3.getResponse(key, false, 0, 7);
expect(s3streamRange.body).toBeTruthy();
let streamContentRange = [];
for await (const chunk of s3streamRange.body) {
// convert Buffer bytes to string
streamContentRange.push(Buffer.from(chunk));
}
const streamContentStringRange = Buffer.concat(streamContentRange).toString('utf-8');
expect(streamContentStringRange).toBe(content.slice(0, 7));

// Verify with Minio client
const minioStream = await new Promise((resolve, reject) => {
minioClient.getObject(testConfigR2.bucketName, key, (err, dataStream) => {
Expand Down

0 comments on commit aee59e1

Please sign in to comment.