├── docs ├── .nojekyll ├── assets │ ├── images │ │ ├── icons.png │ │ ├── icons@2x.png │ │ ├── widgets.png │ │ └── widgets@2x.png │ ├── js │ │ └── search.js │ └── css │ │ └── main.css.map ├── globals.html ├── modules │ ├── _manageddownloaderstream_.html │ └── _manageddownloader_.html ├── interfaces │ ├── _manageddownloader_.manageddownloaderoptions.html │ └── _manageddownloader_.getobjectstreaminput.html ├── index.html └── classes │ └── _manageddownloader_.manageddownloader.html ├── src ├── index.ts ├── ManagedDownloaderStream.ts ├── getRangeOfPart.ts ├── getInformationFromRange.ts ├── ManagedDownloader.ts └── test │ ├── integration │ └── ManagedDownloader.integ.spec.ts │ └── unit │ └── ManagedDownloader.spec.ts ├── .gitignore ├── NOTICE ├── .github └── PULL_REQUEST_TEMPLATE.md ├── .npmignore ├── tsconfig.test.json ├── tsconfig.json ├── CODE_OF_CONDUCT.md ├── package.json ├── CONTRIBUTING.md ├── README.md └── LICENSE /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ManagedDownloader'; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | node_modules 3 | package-lock.json 4 | coverage/* 5 | .DS_Store -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | AWS SDK Js S3 Managed Download 2 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. -------------------------------------------------------------------------------- /docs/assets/images/icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/s3-managed-download-js/HEAD/docs/assets/images/icons.png -------------------------------------------------------------------------------- /docs/assets/images/icons@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/s3-managed-download-js/HEAD/docs/assets/images/icons@2x.png -------------------------------------------------------------------------------- /docs/assets/images/widgets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/s3-managed-download-js/HEAD/docs/assets/images/widgets.png -------------------------------------------------------------------------------- /docs/assets/images/widgets@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/awslabs/s3-managed-download-js/HEAD/docs/assets/images/widgets@2x.png -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | *Issue #, if available:* 2 | 3 | *Description of changes:* 4 | 5 | 6 | By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license. 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | coverage/* 4 | .DS_Store 5 | CODE_OF_CONDUCT.md 6 | CONTRIBUTING.md 7 | tsconfig.* 8 | src/* 9 | docs/* 10 | build/*.spec 11 | .github/* 12 | LICENSE 13 | NOTICE 14 | .gitignore 15 | .npmignore -------------------------------------------------------------------------------- /tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "sourceMap": false, 5 | "inlineSourceMap": true, 6 | "inlineSources": true, 7 | "rootDir": "./src", 8 | "outDir": "./build" 9 | } 10 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2015", 4 | "module": "commonjs", 5 | "declaration": true, 6 | "sourceMap": true, 7 | "strict": true, 8 | "lib": [ 9 | "es2015" 10 | ], 11 | "rootDir": "./src", 12 | "outDir": "./build", 13 | } 14 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /src/ManagedDownloaderStream.ts: -------------------------------------------------------------------------------- 1 | import { PassThrough } from 'stream'; 2 | 3 | /** 4 | * ManagedDownloaderStream is a PassThrough 5 | * stream to pass data from S3 to a client 6 | * and contain useful headers. 7 | */ 8 | export class ManagedDownloaderStream extends PassThrough { 9 | /** 10 | * @param mimeType MIME type describing format of the data. 11 | * @param contentLength length of the body in bytes. 12 | */ 13 | mimeType:string|undefined; 14 | contentLength:number|undefined; 15 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@aws/s3-managed-downloader", 3 | "version": "0.9.0", 4 | "description": "Managed Downloader for S3", 5 | "main": "./build/index.js", 6 | "scripts": { 7 | "pretest": "tsc -p tsconfig.test.json", 8 | "test": "jest 'test/unit/*' --coverage", 9 | "integrationtest": "tsc -p tsconfig.test.json && jest test/integration/*", 10 | "documentation": "typedoc --readme README.md --exclude '**/*.spec.ts, **/index.ts, **/getRangeOfPart.ts, **/getInformationFromRange.ts' --out docs --excludeExternals && touch ./docs/.nojekyll", 11 | "build": "tsc -p tsconfig.json", 12 | "prepublishOnly": "tsc -p tsconfig.json" 13 | }, 14 | "dependencies": { 15 | "aws-sdk": "^2.290.0" 16 | }, 17 | "devDependencies": { 18 | "@types/jest": "^20.0.0", 19 | "@types/node": "^10.3.4", 20 | "jest": "^20.0.0", 21 | "typedoc": "^0.11.1", 22 | "typescript": "^3.0.1" 23 | }, 24 | "jest": { 25 | "testEnvironment": "node" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/getRangeOfPart.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Get the range of bytes as a string 3 | * based on the starting part, max 4 | * part size, and contentLength. 5 | * @param partNum the part number. 6 | * @param totalObjectLength length of the file. 7 | * @param maxPartSize the size of the part. 8 | * @param byteOffset offset the start and end 9 | * by this number. This is necessary for accounting 10 | * for the offset when the client provides a range. 11 | * @return string range in proper format. 12 | */ 13 | export function getRangeOfPart( 14 | partNum:number, 15 | totalObjectLength:number, 16 | maxPartSize:number, 17 | byteOffset:number = 0 18 | ):string { 19 | let range:string = ''; 20 | const startByte:number = (partNum * maxPartSize) + byteOffset; 21 | const endByte:number = Math.min(totalObjectLength, (partNum + 1) 22 | * maxPartSize) + byteOffset; 23 | // range is inclusive start and end byte so subtract 1 24 | range = 'bytes=' + startByte + '-' + (endByte - 1); 25 | return range; 26 | } 27 | 28 | -------------------------------------------------------------------------------- /src/getInformationFromRange.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Information on the start byte, end byte, and length 3 | * of a range of bytes. 4 | */ 5 | export interface RangeInfo { 6 | /** 7 | * @param startByte the starting byte of the range. 8 | * @param endByte the last byte of the range. 9 | * @param length the length of the range. 10 | */ 11 | startByte:number, 12 | endByte:number, 13 | length:number 14 | } 15 | 16 | /** 17 | * Find important information from the user provided range. 18 | * @param range client provided string range in format 19 | * bytes=x-y. 20 | * @return RangeInfo the start byte, end byte, and length 21 | * of the part. 22 | */ 23 | export function getInformationFromRange(range:string):RangeInfo { 24 | const regExp:RegExp = /^bytes=([0-9]+)-([0-9]+)$/; 25 | const bytes:RegExpExecArray|null = regExp.exec(range); 26 | if (bytes == null) { 27 | throw new Error("Invalid Range provided by client"); 28 | } 29 | return { 30 | startByte:parseInt(bytes[1]), 31 | endByte:parseInt(bytes[2]), 32 | length:parseInt(bytes[2]) - parseInt(bytes[1]) 33 | } 34 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check [existing open](https://github.com/awslabs/aws-sdk-js-s3-transfer-manager/issues), or [recently closed](https://github.com/awslabs/aws-sdk-js-s3-transfer-manager/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *master* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels ((enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/awslabs/aws-sdk-js-s3-transfer-manager/labels/help%20wanted) issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](https://github.com/awslabs/aws-sdk-js-s3-transfer-manager/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | 61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS SDK JavaScript S3 Managed Download 2 | 3 | The JavaScript SDK Managed Download can be used for custom S3 downloads. The client can configure options for the Managed Download such as chunk size and number of concurrent transfers. 4 | 5 | ## Installing 6 | 7 | ### In Node.js 8 | 9 | This package only works in Node.js versions 6+ currently. Use the npm package manager for Node.js to install the Managed Download package. Type the following command in a terminal window. 10 | 11 | npm install @aws/s3-managed-download 12 | 13 | ## Documentation 14 | 15 | You can find the full documentation for the Managed Download package [here](https://awslabs.github.io/s3-managed-download-js/). 16 | 17 | ## Configuration 18 | 19 | You can customize the part size and number of concurrent downloads for the Managed Download by setting the maxPartSize and maxConcurrency options. The maxPartSize option controls the size in bytes of each part of the transfer. The maxConcurrency option specifies the number of parts that will be downloaded in parallel and the maximum number of parts that will be buffered in memory at any time. 20 | 21 | ### Import the Managed Download package 22 | 23 | You will need an S3 client in order to create a managed download, so import the aws-sdk along with the managed download package. 24 | 25 | #### JavaScript 26 | 27 | const S3 = require('aws-sdk/clients/s3'); 28 | const ManagedDownloader = require('@aws/s3-managed-download').ManagedDownloader; 29 | 30 | #### TypeScript 31 | 32 | import * as S3 from 'aws-sdk/clients/s3'; 33 | import { ManagedDownloader, GetObjectStreamInput, ManagedDownloaderOptions } from '@aws/s3-managed-download'; 34 | 35 | ### Create a Managed Download with the default part size and concurrency 36 | 37 | Create an AWS S3 client and pass the client into the Managed Download constructor to create a Managed Download with a part size of 5MB and concurrency of 1. 38 | 39 | #### JavaScript 40 | 41 | const s3 = new S3(); 42 | const managedDownloader = new ManagedDownloader(s3); 43 | 44 | #### TypeScript 45 | 46 | const s3:S3 = new S3(); 47 | const managedDownloader:ManagedDownloader = new ManagedDownloader(s3); 48 | 49 | ### Create a Managed Download with custom part size and concurrency 50 | 51 | Create an AWS S3 client and Managed Download options. Pass the client and the options into the Managed Download constructor to create a Managed Download with a custom part size of 10MB and concurrency of 5. 52 | 53 | #### JavaScript 54 | 55 | const s3 = new S3(); 56 | const options = { 57 | maxPartSize: 10 * 1024 * 1024, 58 | maxConcurrency: 5 59 | }; 60 | const managedDownloader = new ManagedDownloader(s3, options); 61 | 62 | #### TypeScript 63 | 64 | const s3:S3 = new S3(); 65 | const options:ManagedDownloaderOptons = { 66 | maxPartSize: 10 * 1024 * 1024, 67 | maxConcurrency: 5 68 | }; 69 | const managedDownloader:ManagedDownloader = new ManagedDownloader(s3, options); 70 | 71 | ## Examples 72 | 73 | Currently, the Managed Download only contains a streaming download operation which can work for an entire file or a specific range or part of a file. Here are some examples on 74 | how to use the Managed Download's getObjectStream download operation. 75 | 76 | 77 | 78 | ### 1. Use the Managed Download object to create a download stream for a file on S3 79 | 80 | Get the file 'example-key' from the bucket 'example-bucket' and use the getObjectStream method to create a local file at 'example-file-path'. 81 | 82 | #### JavaScript 83 | 84 | const S3 = require('aws-sdk/clients/s3'); 85 | const ManagedDownloader = require('@aws/s3-managed-download').ManagedDownloader; 86 | const fs = require('fs'); 87 | 88 | const s3 = new S3(); 89 | const managedDownloader = new ManagedDownloader(s3); 90 | 91 | const params = { 92 | Bucket: 'example-bucket', 93 | Key: 'example-key' 94 | }; 95 | // create a write stream for a file 96 | const writeStream = fs.createWriteStream('example-file-path'); 97 | 98 | managedDownloader.getObjectStream(params) 99 | .then((stream) => { 100 | stream.pipe(writeStream); 101 | }, (err) => { 102 | writeStream.end(); 103 | console.error(err); 104 | }); 105 | 106 | #### TypeScript 107 | 108 | import * as S3 from 'aws-sdk/clients/s3'; 109 | import { ManagedDownloader, GetObjectStreamInput, ManagedDownloaderOptions } from '@aws/s3-managed-download'; 110 | import * as fs from 'fs'; 111 | 112 | const s3:S3 = new S3(); 113 | const managedDownloader:ManagedDownloader = new ManagedDownloader(s3); 114 | 115 | const params:GetObjectStreamInput = { 116 | Bucket: 'example-bucket', 117 | Key: 'example-key' 118 | }; 119 | // create a write stream for a file 120 | const writeStream:fs.WriteStream = fs.createWriteStream('example-file-path'); 121 | 122 | managedDownloader.getObjectStream(params) 123 | .then((stream) => { 124 | stream.pipe(writeStream); 125 | }, (err) => { 126 | writeStream.end(); 127 | console.error(err); 128 | }); 129 | 130 | ### 2. Use the Managed Download object to create a download stream for a range of bytes of a file on S3 131 | 132 | Get the file 'example-key' from the bucket 'example-bucket' and use the getObjectStream method to write to bytes 100-150 of a local file at 'example-file-path'. 133 | 134 | #### JavaScript 135 | 136 | const S3 = require('aws-sdk/clients/s3'); 137 | const ManagedDownloader = require('@aws/s3-managed-download').ManagedDownloader; 138 | const fs = require('fs'); 139 | 140 | const s3 = new S3(); 141 | const managedDownloader = new ManagedDownloader(s3); 142 | 143 | const params = { 144 | Bucket: 'example-bucket', 145 | Key: 'example-key', 146 | Range: '100-150' 147 | }; 148 | // create a write stream for a file starting at byte 100 149 | const writeStream = fs.createWriteStream('example-file-path', {start:100}); 150 | 151 | managedDownloader.getObjectStream(params) 152 | .then((stream) => { 153 | stream.pipe(writeStream); 154 | }, (err) => { 155 | writeStream.end(); 156 | console.error(err); 157 | }); 158 | 159 | #### TypeScript 160 | 161 | import * as S3 from 'aws-sdk/clients/s3'; 162 | import { ManagedDownloader, GetObjectStreamInput, ManagedDownloaderOptions } from '@aws/s3-managed-download'; 163 | import * as fs from 'fs'; 164 | 165 | const s3:S3 = new S3(); 166 | const managedDownloader:ManagedDownloader = new ManagedDownloader(s3); 167 | 168 | const params:GetObjectStreamInput = { 169 | Bucket: 'example-bucket', 170 | Key: 'example-key', 171 | Range: '100-150' 172 | }; 173 | // create a write stream for a file starting at byte 100 174 | const writeStream:fs.WriteStream = fs.createWriteStream('example-file-path', {start:100}); 175 | 176 | managedDownloader.getObjectStream(params) 177 | .then((stream) => { 178 | stream.pipe(writeStream); 179 | }, (err) => { 180 | writeStream.end(); 181 | console.error(err); 182 | }); 183 | 184 | ## Opening Issues 185 | 186 | If you find any bugs or want to request a new feature, please first check the existing issues. If there is not already an issue, then open a new issue. If the issue is regarding a bug, please include the stack trace and a method to recreate it. 187 | 188 | ## License 189 | 190 | This library is licensed under the Apache 2.0 License. 191 | -------------------------------------------------------------------------------- /docs/globals.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | @aws/s3-managed-downloader 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
47 |
48 |
49 |
50 |
51 |
52 | 57 |

@aws/s3-managed-downloader

58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |

Index

66 |
67 |
68 |
69 |

External modules

70 | 74 |
75 |
76 |
77 |
78 |
79 | 98 |
99 |
100 | 159 |
160 |

Generated using TypeDoc

161 |
162 |
163 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /src/ManagedDownloader.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The ManagedDownloader uses an s3 client for custom efficient 3 | * downloads from s3. 4 | */ 5 | import * as S3 from 'aws-sdk/clients/s3'; 6 | import { getRangeOfPart } from './getRangeOfPart'; 7 | import { getInformationFromRange } from './getInformationFromRange'; 8 | import { ManagedDownloaderStream } from './ManagedDownloaderStream'; 9 | 10 | /** 11 | * Custom options for the Managed Download. 12 | */ 13 | export interface ManagedDownloaderOptions { 14 | /** 15 | * @param maxPartSize size of a part in bytes. 16 | * @param maxConcurrency number of parts to download 17 | * in parallel. 18 | */ 19 | maxPartSize?:number, 20 | maxConcurrency?:number 21 | } 22 | 23 | /** 24 | * Parameter type for the getObjectStream function. 25 | */ 26 | export interface GetObjectStreamInput { 27 | /** 28 | * @param Bucket S3 bucket of a file. 29 | * @param Key key of a file in an S3 bucket. 30 | * @param Range range of bytes to download 31 | * in format 'bytes=([0-9]+)-([0-9]+)'. 32 | * @param PartNumber part number of file to download. 33 | * This option is useful for files uploaded using 34 | * the multipart uploader. Other files will only have 35 | * 1 part. 36 | */ 37 | Bucket:string, 38 | Key:string, 39 | Range?:string, 40 | PartNumber?:number 41 | } 42 | 43 | /** 44 | * The ManagedDownloader class handles custom S3 downloads. 45 | * A client can set options for the download such as 46 | * the part size and the concurrency. 47 | */ 48 | export class ManagedDownloader { 49 | 50 | /** 51 | * @param maxPartSize max part size of download in bytes. 52 | * @param maxConcurrency maximum parallel downloads. 53 | */ 54 | private readonly maxPartSize:number 55 | private readonly maxConcurrency:number 56 | 57 | /** 58 | * Create a ManagedDownloader object with an 59 | * s3 client and custom options if provided. 60 | * 61 | * @param client an S3 client to make requests. 62 | * @param options configuration for the managed download. 63 | * @throws Error if the client is not provided. 64 | * @throws Error if maxPartSize is not a positive number. 65 | * @throws Error if concurrency is not a positive number. 66 | */ 67 | constructor(private readonly client:S3, options:ManagedDownloaderOptions = {}) { 68 | if (!client) { 69 | throw new Error('S3 client must be provided'); 70 | } 71 | if ( 72 | (options && options.maxPartSize !== undefined) && 73 | (typeof options.maxPartSize !== "number" || options.maxPartSize < 1) 74 | ) { 75 | throw new Error( 76 | 'Maximum part size must be a positive number' 77 | ); 78 | } 79 | if ( 80 | (options && options.maxConcurrency !== undefined) && 81 | (typeof options.maxConcurrency !== "number" || options.maxConcurrency < 1) 82 | ) { 83 | throw new Error( 84 | 'Maximum concurrency must be a positive number' 85 | ); 86 | } 87 | this.maxPartSize = options.maxPartSize || 1024*1024*5; 88 | this.maxConcurrency = options.maxConcurrency || 1; 89 | } 90 | 91 | /** 92 | * Get a download stream for a file stored in s3. 93 | * Download the first part of size [maxPartSize] to get 94 | * the content length and set headers for the stream. 95 | * If the file length is less than [maxPartSize], or the 96 | * client provides a part number, then return the stream. 97 | * If not, call downloadByParts to split the rest of the file into 98 | * parts of size [maxPartSize] and download [maxConcurrency] parts 99 | * in parallel. 100 | * @param params an object in the format 101 | * { 102 | * Bucket:string, 103 | * Key:string, 104 | * Range(optional):string, 105 | * PartNumber(optional):number 106 | * }. 107 | * @return ManagedDownloaderStream the PassThrough stream to read 108 | * file data from. 109 | * @throws Error if params is in the incorrect format. 110 | */ 111 | async getObjectStream( 112 | params: GetObjectStreamInput 113 | ):Promise { 114 | const destinationStream = new ManagedDownloaderStream(); 115 | let contentLength; 116 | let byteOffset; 117 | if (typeof(params) !== 'object' || !params.Bucket || !params.Key) { 118 | throw new Error( 119 | `expected an object in the format {Bucket:string, Key:string, 120 | Range(optional):string, PartNumber(optional):number}, 121 | received ${params}`); 122 | } 123 | if (params.Range && !params.PartNumber) { 124 | const rangeInfo = getInformationFromRange(params.Range); 125 | contentLength = rangeInfo.length; 126 | byteOffset = rangeInfo.startByte; 127 | params.Range = getRangeOfPart( 128 | 0, contentLength, 129 | this.maxPartSize, byteOffset 130 | ); 131 | } else if (!params.PartNumber) { 132 | params.Range = 'bytes=0-' + (this.maxPartSize - 1); 133 | } 134 | const getObjectData:S3.GetObjectOutput = 135 | await this.client.getObject(params).promise(); 136 | contentLength = contentLength || 137 | parseInt(getObjectData.ContentRange!.split('/')[1]); 138 | destinationStream.mimeType = getObjectData.ContentType; 139 | destinationStream.contentLength = contentLength; 140 | destinationStream.write(getObjectData.Body); 141 | if (contentLength > this.maxPartSize 142 | && !params.PartNumber) { 143 | this.downloadByParts( 144 | params, destinationStream, 145 | contentLength, byteOffset, 1 146 | ); 147 | } else { 148 | destinationStream.end(); 149 | } 150 | return destinationStream; 151 | } 152 | 153 | /** 154 | * Download the individual parts of the file 155 | * and write them to the stream sequentially. 156 | * Download n = this.maxConcurrency parts 157 | * in parallel and store parts which 158 | * aren't the next part in memory until the 159 | * stream is ready to read it. 160 | * 161 | * @param params params object contains the bucket 162 | * and key. 163 | * @param destinationStream the ManagedDownloaderStream stream 164 | * transferring the parts of a file. 165 | * @param contentLength the length of the file. 166 | * @param byteOffset if available, the amount to add to start 167 | * and end byte when calculating the range. 168 | * @param startingPart the part number to start downloading from. 169 | */ 170 | private async downloadByParts( 171 | params:GetObjectStreamInput, 172 | destinationStream:ManagedDownloaderStream, 173 | contentLength:number, 174 | byteOffset:number = 0, 175 | startingPart:number = 0 176 | ):Promise { 177 | const numParts:number = Math.ceil( 178 | contentLength/this.maxPartSize 179 | ); 180 | const queue:Promise[] = []; 181 | let numDownloads = 0; 182 | let currentPart = startingPart; 183 | while (numDownloads < numParts - startingPart) { 184 | try { 185 | const numRequests = Math.min( 186 | this.maxConcurrency - queue.length, 187 | numParts - currentPart 188 | ); 189 | for (let i = 0; i < numRequests; i++) { 190 | params.Range = getRangeOfPart( 191 | currentPart, contentLength, this.maxPartSize, byteOffset 192 | ); 193 | queue.push( 194 | this.client.getObject(params).promise() 195 | ); 196 | currentPart++; 197 | } 198 | const result:S3.GetObjectOutput = await queue.shift()!; 199 | if (!destinationStream.write(result.Body)) { 200 | await this.waitForDrainEvent(destinationStream); 201 | }; 202 | numDownloads++; 203 | } catch(err) { 204 | destinationStream.emit('error', err); 205 | return; 206 | } 207 | } 208 | destinationStream.end(); 209 | } 210 | 211 | /** 212 | * wait for a stream to emit a 213 | * drain event when it is ready to 214 | * receive data again. 215 | * @param destinationStream the stream 216 | * that is being watched for the drain 217 | * event. 218 | */ 219 | private waitForDrainEvent( 220 | destinationStream:ManagedDownloaderStream 221 | ):Promise { 222 | return new Promise((resolve) => { 223 | destinationStream.once('drain', () => { 224 | resolve(); 225 | }); 226 | }); 227 | } 228 | } 229 | 230 | -------------------------------------------------------------------------------- /docs/modules/_manageddownloaderstream_.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | "ManagedDownloaderStream" | @aws/s3-managed-downloader 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
47 |
48 |
49 |
50 |
51 |
52 | 60 |

External module "ManagedDownloaderStream"

61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |

Index

69 |
70 |
71 |
72 |

Classes

73 | 76 |
77 |
78 |
79 |
80 |
81 | 103 |
104 |
105 |
106 |
107 |

Legend

108 |
109 |
    110 |
  • Module
  • 111 |
  • Object literal
  • 112 |
  • Variable
  • 113 |
  • Function
  • 114 |
  • Function with type parameter
  • 115 |
  • Index signature
  • 116 |
  • Type alias
  • 117 |
118 |
    119 |
  • Enumeration
  • 120 |
  • Enumeration member
  • 121 |
  • Property
  • 122 |
  • Method
  • 123 |
124 |
    125 |
  • Interface
  • 126 |
  • Interface with type parameter
  • 127 |
  • Constructor
  • 128 |
  • Property
  • 129 |
  • Method
  • 130 |
  • Index signature
  • 131 |
132 |
    133 |
  • Class
  • 134 |
  • Class with type parameter
  • 135 |
  • Constructor
  • 136 |
  • Property
  • 137 |
  • Method
  • 138 |
  • Accessor
  • 139 |
  • Index signature
  • 140 |
141 |
    142 |
  • Inherited constructor
  • 143 |
  • Inherited property
  • 144 |
  • Inherited method
  • 145 |
  • Inherited accessor
  • 146 |
147 |
    148 |
  • Protected property
  • 149 |
  • Protected method
  • 150 |
  • Protected accessor
  • 151 |
152 |
    153 |
  • Private property
  • 154 |
  • Private method
  • 155 |
  • Private accessor
  • 156 |
157 |
    158 |
  • Static property
  • 159 |
  • Static method
  • 160 |
161 |
162 |
163 |
164 |
165 |

Generated using TypeDoc

166 |
167 |
168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /docs/modules/_manageddownloader_.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | "ManagedDownloader" | @aws/s3-managed-downloader 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
47 |
48 |
49 |
50 |
51 |
52 | 60 |

External module "ManagedDownloader"

61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |

Index

69 |
70 |
71 |
72 |

Classes

73 | 76 |
77 |
78 |

Interfaces

79 | 83 |
84 |
85 |
86 |
87 |
88 | 116 |
117 |
118 |
119 |
120 |

Legend

121 |
122 |
    123 |
  • Module
  • 124 |
  • Object literal
  • 125 |
  • Variable
  • 126 |
  • Function
  • 127 |
  • Function with type parameter
  • 128 |
  • Index signature
  • 129 |
  • Type alias
  • 130 |
131 |
    132 |
  • Enumeration
  • 133 |
  • Enumeration member
  • 134 |
  • Property
  • 135 |
  • Method
  • 136 |
137 |
    138 |
  • Interface
  • 139 |
  • Interface with type parameter
  • 140 |
  • Constructor
  • 141 |
  • Property
  • 142 |
  • Method
  • 143 |
  • Index signature
  • 144 |
145 |
    146 |
  • Class
  • 147 |
  • Class with type parameter
  • 148 |
  • Constructor
  • 149 |
  • Property
  • 150 |
  • Method
  • 151 |
  • Accessor
  • 152 |
  • Index signature
  • 153 |
154 |
    155 |
  • Inherited constructor
  • 156 |
  • Inherited property
  • 157 |
  • Inherited method
  • 158 |
  • Inherited accessor
  • 159 |
160 |
    161 |
  • Protected property
  • 162 |
  • Protected method
  • 163 |
  • Protected accessor
  • 164 |
165 |
    166 |
  • Private property
  • 167 |
  • Private method
  • 168 |
  • Private accessor
  • 169 |
170 |
    171 |
  • Static property
  • 172 |
  • Static method
  • 173 |
174 |
175 |
176 |
177 |
178 |

Generated using TypeDoc

179 |
180 |
181 | 182 | 183 | 184 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /src/test/integration/ManagedDownloader.integ.spec.ts: -------------------------------------------------------------------------------- 1 | import { ManagedDownloader, GetObjectStreamInput } from '../../ManagedDownloader'; 2 | import * as S3 from 'aws-sdk/clients/s3'; 3 | import { Stream, Writable } from 'stream'; 4 | import * as crypto from 'crypto'; 5 | 6 | // create an S3 client for use in tests 7 | const s3client:S3 = new S3({region:'us-west-2'}); 8 | 9 | describe('getObjectStream tests', () => { 10 | 11 | const testBucketName:string = 's3-managed-download-test-bucket' + Date.now(); 12 | const testFileName:string = 's3-managed-download-test.txt'; 13 | let fileChecksum:string|undefined; 14 | let rangeChecksum:string|undefined; 15 | let fileLength = 0; 16 | let originalTimeout:number; 17 | 18 | beforeAll(async() => { 19 | const entireFileHash:crypto.Hash = crypto.createHash('md5'); 20 | const rangeFileHash:crypto.Hash = crypto.createHash('md5'); 21 | originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; 22 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000; 23 | let fileBody:string = ""; 24 | const body:string = " test test test "; 25 | for (let i = 0; i < 1000; i++) { 26 | fileBody += body; 27 | fileLength += body.length; 28 | entireFileHash.update(body); 29 | if (i >= 200 && i < 800) { 30 | rangeFileHash.update(body); 31 | } 32 | } 33 | fileChecksum = entireFileHash.digest('hex'); 34 | rangeChecksum = rangeFileHash.digest('hex'); 35 | const params:S3.PutObjectRequest = { 36 | Bucket: testBucketName, 37 | Key: testFileName, 38 | Body: fileBody 39 | } 40 | await s3client.createBucket({Bucket:testBucketName}).promise(); 41 | await s3client.waitFor('bucketExists', {Bucket:testBucketName}); 42 | await s3client.putObject(params).promise(); 43 | }); 44 | 45 | afterAll(async() => { 46 | const params:S3.DeleteObjectRequest = { 47 | Bucket: testBucketName, 48 | Key: testFileName, 49 | } 50 | jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; 51 | await s3client.deleteObject(params).promise(); 52 | await s3client.deleteBucket({Bucket: testBucketName}).promise(); 53 | }); 54 | 55 | it('will return accurate data through the stream', async() => { 56 | const source:GetObjectStreamInput = { 57 | Key:testFileName, 58 | Bucket:testBucketName 59 | }; 60 | // this maxPartSize option will split the file into 4 parts 61 | const managedDownloader:ManagedDownloader = new ManagedDownloader(s3client, {maxPartSize:getMaxPartSize(4, fileLength)}); 62 | let s3Checksum:string; 63 | let s3Promise = promisifyStreamOutput( 64 | await managedDownloader.getObjectStream(source)); 65 | s3Checksum = await s3Promise; 66 | expect(s3Checksum).toBe(fileChecksum); 67 | }); 68 | 69 | it ('will return accurate data through the stream when the file is only 1 part', async() => { 70 | const source:GetObjectStreamInput = { 71 | Key:testFileName, 72 | Bucket:testBucketName 73 | }; 74 | // this maxPartSize option will split the file into 1 part 75 | const managedDownloader:ManagedDownloader = new ManagedDownloader(s3client, {maxPartSize:getMaxPartSize(1, fileLength)}); 76 | let s3Checksum:string; 77 | let s3Promise = promisifyStreamOutput( 78 | await managedDownloader.getObjectStream(source)); 79 | s3Checksum = await s3Promise; 80 | expect(s3Checksum).toBe(fileChecksum); 81 | }); 82 | 83 | it('will return accurate data through the stream when the client provides a range', async() => { 84 | const source:GetObjectStreamInput = { 85 | Key:testFileName, 86 | Bucket:testBucketName, 87 | Range:"bytes=3200-12800" 88 | }; 89 | // this maxPartSize option will split the file into 4 parts 90 | const managedDownloader:ManagedDownloader = new ManagedDownloader(s3client, {maxPartSize:getMaxPartSize(4, fileLength)}); 91 | let s3Checksum:string; 92 | let s3Promise = promisifyStreamOutput( 93 | await managedDownloader.getObjectStream(source)); 94 | s3Checksum = await s3Promise; 95 | expect(s3Checksum).toBe(rangeChecksum); 96 | }); 97 | 98 | it('will throw an error if the file/bucket does not exist', async() => { 99 | const source:GetObjectStreamInput = { 100 | Key:'doesntexist.txt', 101 | Bucket:'doesntexist' 102 | } 103 | const managedDownloader:ManagedDownloader = new ManagedDownloader(s3client); 104 | let downloadError:Error|undefined; 105 | try { 106 | await managedDownloader.getObjectStream(source); 107 | } catch(err) { 108 | downloadError = err; 109 | } 110 | expect(downloadError).toBeInstanceOf(Error); 111 | }); 112 | 113 | describe('getObjectStream client provides part number', () => { 114 | 115 | const multipartFile = 'multipartUpload.txt'; 116 | let partHash:string|undefined; 117 | 118 | beforeAll(async() => { 119 | const params = { 120 | Bucket: testBucketName, 121 | Key: multipartFile, 122 | Body: '' 123 | } 124 | const options = { 125 | partSize: 5*1024*1024 126 | } 127 | let hash:crypto.Hash = crypto.createHash('md5'); 128 | 129 | for (let i = 0; i < 5*1024*1024; i++) { 130 | params.Body += '01'; 131 | if (i >= (5*1024*1024)/2) { 132 | hash.update('01'); 133 | } 134 | } 135 | partHash = hash.digest('hex'); 136 | await s3client.upload(params, options).promise(); 137 | }); 138 | 139 | afterAll(async() => { 140 | const params = { 141 | Bucket: testBucketName, 142 | Key: multipartFile 143 | } 144 | await s3client.deleteObject(params).promise(); 145 | }); 146 | 147 | it('Will throw an error if the client provides both range and part number', async() => { 148 | const source:GetObjectStreamInput = { 149 | Key:multipartFile, 150 | Bucket:testBucketName, 151 | PartNumber: 1, 152 | Range:'bytes=101-202' 153 | }; 154 | const managedDownloader:ManagedDownloader = new ManagedDownloader(s3client); 155 | let downloadError:Error|undefined; 156 | try { 157 | await managedDownloader.getObjectStream(source); 158 | } catch(err) { 159 | downloadError = err; 160 | } 161 | expect(downloadError).toBeInstanceOf(Error); 162 | }); 163 | 164 | it('Will throw an error if the part number is out of range', async() => { 165 | const source:GetObjectStreamInput = { 166 | Key:multipartFile, 167 | Bucket:testBucketName, 168 | PartNumber: 5 169 | }; 170 | const managedDownloader:ManagedDownloader = new ManagedDownloader(s3client); 171 | let downloadError:Error|undefined; 172 | try { 173 | await managedDownloader.getObjectStream(source); 174 | } catch(err) { 175 | downloadError = err; 176 | } 177 | expect(downloadError).toBeInstanceOf(Error); 178 | }); 179 | 180 | it(`will return accurate data through the stream when a part number is provided if the file 181 | was not uploaded using a multipart uploader`, async() => { 182 | const source:GetObjectStreamInput = { 183 | Key:testFileName, 184 | Bucket:testBucketName, 185 | PartNumber: 1 186 | }; 187 | const managedDownloader:ManagedDownloader = new ManagedDownloader(s3client, {maxPartSize:getMaxPartSize(4, fileLength)}); 188 | let s3Checksum:string; 189 | let s3Promise = promisifyStreamOutput( 190 | await managedDownloader.getObjectStream(source)); 191 | s3Checksum = await s3Promise; 192 | expect(s3Checksum).toBe(fileChecksum); 193 | }); 194 | 195 | it('will download given part of file if the file is uploaded using the multipart uploader', async() => { 196 | const source:GetObjectStreamInput = { 197 | Key:multipartFile, 198 | Bucket:testBucketName, 199 | PartNumber: 2 200 | }; 201 | const managedDownloader:ManagedDownloader = new ManagedDownloader(s3client, {maxPartSize:1024*4}); 202 | let s3Checksum:string; 203 | let s3Promise = promisifyStreamOutput( 204 | await managedDownloader.getObjectStream(source)); 205 | s3Checksum = await s3Promise; 206 | expect(s3Checksum).toBe(partHash); 207 | }); 208 | 209 | }); 210 | 211 | describe('getObjectStream client provides concurrency', () => { 212 | // the number of parts will range from 2-5 because maxConcurrency can't be less than 1 213 | const numParts = 4; 214 | it('will return accurate data through the stream with number of parts less than maxConcurrency', async() => { 215 | const source:GetObjectStreamInput = { 216 | Key:testFileName, 217 | Bucket:testBucketName 218 | }; 219 | const managedDownloader:ManagedDownloader = 220 | new ManagedDownloader(s3client, {maxPartSize:getMaxPartSize(Math.floor(numParts/2), fileLength),maxConcurrency:numParts}); 221 | let s3Checksum:string; 222 | let s3Promise = promisifyStreamOutput( 223 | await managedDownloader.getObjectStream(source)); 224 | s3Checksum = await s3Promise; 225 | expect(s3Checksum).toBe(fileChecksum); 226 | }); 227 | 228 | it('will return accurate data through the stream with number of parts equal to maxConcurrency', async() => { 229 | const source:GetObjectStreamInput = { 230 | Key:testFileName, 231 | Bucket:testBucketName 232 | }; 233 | const managedDownloader:ManagedDownloader = 234 | new ManagedDownloader(s3client, {maxPartSize:getMaxPartSize(numParts, fileLength),maxConcurrency:numParts}); 235 | let s3Checksum:string; 236 | let s3Promise = promisifyStreamOutput( 237 | await managedDownloader.getObjectStream(source)); 238 | s3Checksum = await s3Promise; 239 | expect(s3Checksum).toBe(fileChecksum); 240 | }); 241 | 242 | it('will return accurate data through the stream with number of parts greater than maxConcurrency', async() => { 243 | const source:GetObjectStreamInput = { 244 | Key:testFileName, 245 | Bucket:testBucketName 246 | }; 247 | const managedDownloader:ManagedDownloader = 248 | new ManagedDownloader(s3client, {maxPartSize:getMaxPartSize(numParts*2, fileLength),maxConcurrency:numParts}); 249 | let s3Checksum:string; 250 | let s3Promise = promisifyStreamOutput( 251 | await managedDownloader.getObjectStream(source)); 252 | s3Checksum = await s3Promise; 253 | expect(s3Checksum).toBe(fileChecksum); 254 | }); 255 | 256 | }); 257 | 258 | }); 259 | 260 | function promisifyStreamOutput(stream:Stream):Promise { 261 | let hash:crypto.Hash = crypto.createHash('md5'); 262 | return new Promise((resolve:(data:string) => void, 263 | reject:(err:Error) => void) => { 264 | stream 265 | .on('data', (chunk) => { 266 | hash.update(chunk, 'utf8'); 267 | }) 268 | .on('end', () => { 269 | resolve(hash.digest('hex')); 270 | }) 271 | .on('error', (err) => { 272 | reject(err); 273 | }); 274 | }); 275 | } 276 | 277 | function getMaxPartSize(numParts:number, length:number):number { 278 | return Math.ceil(length/numParts); 279 | } -------------------------------------------------------------------------------- /docs/interfaces/_manageddownloader_.manageddownloaderoptions.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ManagedDownloaderOptions | @aws/s3-managed-downloader 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
47 |
48 |
49 |
50 |
51 |
52 | 63 |

Interface ManagedDownloaderOptions

64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |

Custom options for the Managed Download.

74 |
75 |
76 |
77 |
78 |

Hierarchy

79 |
    80 |
  • 81 | ManagedDownloaderOptions 82 |
  • 83 |
84 |
85 |
86 |

Index

87 |
88 |
89 |
90 |

Properties

91 | 95 |
96 |
97 |
98 |
99 |
100 |

Properties

101 |
102 | 103 |

Optional maxConcurrency

104 |
maxConcurrency: undefined | number
105 | 110 |
111 |
112 | 113 |

Optional maxPartSize

114 |
maxPartSize: undefined | number
115 | 120 |
121 |
122 |
param
123 |

size of a part in bytes.

124 |
125 |
param
126 |

number of parts to download 127 | in parallel.

128 |
129 |
130 |
131 |
132 |
133 |
134 | 174 |
175 |
176 |
177 |
178 |

Legend

179 |
180 |
    181 |
  • Module
  • 182 |
  • Object literal
  • 183 |
  • Variable
  • 184 |
  • Function
  • 185 |
  • Function with type parameter
  • 186 |
  • Index signature
  • 187 |
  • Type alias
  • 188 |
189 |
    190 |
  • Enumeration
  • 191 |
  • Enumeration member
  • 192 |
  • Property
  • 193 |
  • Method
  • 194 |
195 |
    196 |
  • Interface
  • 197 |
  • Interface with type parameter
  • 198 |
  • Constructor
  • 199 |
  • Property
  • 200 |
  • Method
  • 201 |
  • Index signature
  • 202 |
203 |
    204 |
  • Class
  • 205 |
  • Class with type parameter
  • 206 |
  • Constructor
  • 207 |
  • Property
  • 208 |
  • Method
  • 209 |
  • Accessor
  • 210 |
  • Index signature
  • 211 |
212 |
    213 |
  • Inherited constructor
  • 214 |
  • Inherited property
  • 215 |
  • Inherited method
  • 216 |
  • Inherited accessor
  • 217 |
218 |
    219 |
  • Protected property
  • 220 |
  • Protected method
  • 221 |
  • Protected accessor
  • 222 |
223 |
    224 |
  • Private property
  • 225 |
  • Private method
  • 226 |
  • Private accessor
  • 227 |
228 |
    229 |
  • Static property
  • 230 |
  • Static method
  • 231 |
232 |
233 |
234 |
235 |
236 |

Generated using TypeDoc

237 |
238 |
239 | 240 | 241 | 242 | -------------------------------------------------------------------------------- /docs/interfaces/_manageddownloader_.getobjectstreaminput.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | GetObjectStreamInput | @aws/s3-managed-downloader 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
47 |
48 |
49 |
50 |
51 |
52 | 63 |

Interface GetObjectStreamInput

64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |

Parameter type for the getObjectStream function.

74 |
75 |
76 |
77 |
78 |

Hierarchy

79 |
    80 |
  • 81 | GetObjectStreamInput 82 |
  • 83 |
84 |
85 |
86 |

Index

87 |
88 |
89 |
90 |

Properties

91 | 97 |
98 |
99 |
100 |
101 |
102 |

Properties

103 |
104 | 105 |

Bucket

106 |
Bucket: string
107 | 112 |
113 |
114 |
param
115 |

S3 bucket of a file.

116 |
117 |
param
118 |

key of a file in an S3 bucket.

119 |
120 |
param
121 |

range of bytes to download 122 | in format 'bytes=([0-9]+)-([0-9]+)'.

123 |
124 |
param
125 |

part number of file to download. 126 | This option is useful for files uploaded using 127 | the multipart uploader. Other files will only have 128 | 1 part.

129 |
130 |
131 |
132 |
133 |
134 | 135 |

Key

136 |
Key: string
137 | 142 |
143 |
144 | 145 |

Optional PartNumber

146 |
PartNumber: undefined | number
147 | 152 |
153 |
154 | 155 |

Optional Range

156 |
Range: undefined | string
157 | 162 |
163 |
164 |
165 | 211 |
212 |
213 |
214 |
215 |

Legend

216 |
217 |
    218 |
  • Module
  • 219 |
  • Object literal
  • 220 |
  • Variable
  • 221 |
  • Function
  • 222 |
  • Function with type parameter
  • 223 |
  • Index signature
  • 224 |
  • Type alias
  • 225 |
226 |
    227 |
  • Enumeration
  • 228 |
  • Enumeration member
  • 229 |
  • Property
  • 230 |
  • Method
  • 231 |
232 |
    233 |
  • Interface
  • 234 |
  • Interface with type parameter
  • 235 |
  • Constructor
  • 236 |
  • Property
  • 237 |
  • Method
  • 238 |
  • Index signature
  • 239 |
240 |
    241 |
  • Class
  • 242 |
  • Class with type parameter
  • 243 |
  • Constructor
  • 244 |
  • Property
  • 245 |
  • Method
  • 246 |
  • Accessor
  • 247 |
  • Index signature
  • 248 |
249 |
    250 |
  • Inherited constructor
  • 251 |
  • Inherited property
  • 252 |
  • Inherited method
  • 253 |
  • Inherited accessor
  • 254 |
255 |
    256 |
  • Protected property
  • 257 |
  • Protected method
  • 258 |
  • Protected accessor
  • 259 |
260 |
    261 |
  • Private property
  • 262 |
  • Private method
  • 263 |
  • Private accessor
  • 264 |
265 |
    266 |
  • Static property
  • 267 |
  • Static method
  • 268 |
269 |
270 |
271 |
272 |
273 |

Generated using TypeDoc

274 |
275 |
276 | 277 | 278 | 279 | -------------------------------------------------------------------------------- /docs/assets/js/search.js: -------------------------------------------------------------------------------- 1 | var typedoc = typedoc || {}; 2 | typedoc.search = typedoc.search || {}; 3 | typedoc.search.data = {"kinds":{"1":"External module","128":"Class","256":"Interface","512":"Constructor","1024":"Property","2048":"Method"},"rows":[{"id":0,"kind":1,"name":"\"ManagedDownloaderStream\"","url":"modules/_manageddownloaderstream_.html","classes":"tsd-kind-external-module"},{"id":1,"kind":128,"name":"ManagedDownloaderStream","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html","classes":"tsd-kind-class tsd-parent-kind-external-module","parent":"\"ManagedDownloaderStream\""},{"id":2,"kind":1024,"name":"mimeType","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#mimetype","classes":"tsd-kind-property tsd-parent-kind-class","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":3,"kind":1024,"name":"contentLength","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#contentlength","classes":"tsd-kind-property tsd-parent-kind-class","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":4,"kind":512,"name":"constructor","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#constructor","classes":"tsd-kind-constructor tsd-parent-kind-class tsd-is-overwrite tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":5,"kind":2048,"name":"_transform","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#_transform","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":6,"kind":2048,"name":"_flush","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#_flush","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":7,"kind":1024,"name":"writable","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#writable","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":8,"kind":1024,"name":"writableHighWaterMark","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#writablehighwatermark","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":9,"kind":1024,"name":"writableLength","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#writablelength","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":10,"kind":2048,"name":"_write","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#_write","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":11,"kind":2048,"name":"_writev","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#_writev","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":12,"kind":2048,"name":"_destroy","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#_destroy","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-overwrite tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":13,"kind":2048,"name":"_final","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#_final","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":14,"kind":2048,"name":"write","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#write","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":15,"kind":2048,"name":"setDefaultEncoding","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#setdefaultencoding","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":16,"kind":2048,"name":"end","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#end","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":17,"kind":2048,"name":"cork","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#cork","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":18,"kind":2048,"name":"uncork","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#uncork","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":19,"kind":1024,"name":"readable","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#readable","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":20,"kind":1024,"name":"readableHighWaterMark","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#readablehighwatermark","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":21,"kind":1024,"name":"readableLength","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#readablelength","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":22,"kind":2048,"name":"_read","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#_read","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":23,"kind":2048,"name":"read","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#read","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":24,"kind":2048,"name":"setEncoding","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#setencoding","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":25,"kind":2048,"name":"pause","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#pause","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":26,"kind":2048,"name":"resume","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#resume","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":27,"kind":2048,"name":"isPaused","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#ispaused","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":28,"kind":2048,"name":"unpipe","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#unpipe","classes":"tsd-kind-method tsd-parent-kind-class tsd-has-type-parameter tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":29,"kind":2048,"name":"unshift","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#unshift","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":30,"kind":2048,"name":"wrap","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#wrap","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":31,"kind":2048,"name":"push","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#push","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":32,"kind":2048,"name":"destroy","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#destroy","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":33,"kind":2048,"name":"addListener","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#addlistener","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-overwrite tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":34,"kind":2048,"name":"emit","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#emit","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-overwrite tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":35,"kind":2048,"name":"on","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#on","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-overwrite tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":36,"kind":2048,"name":"once","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#once","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-overwrite tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":37,"kind":2048,"name":"prependListener","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#prependlistener","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-overwrite tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":38,"kind":2048,"name":"prependOnceListener","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#prependoncelistener","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-overwrite tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":39,"kind":2048,"name":"removeListener","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#removelistener","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-overwrite tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":40,"kind":2048,"name":"__@asyncIterator","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#___asynciterator","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":41,"kind":2048,"name":"pipe","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#pipe","classes":"tsd-kind-method tsd-parent-kind-class tsd-has-type-parameter tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":42,"kind":2048,"name":"listenerCount","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#listenercount-1","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-inherited tsd-is-static","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":43,"kind":1024,"name":"defaultMaxListeners","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#defaultmaxlisteners","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-inherited tsd-is-static","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":44,"kind":2048,"name":"off","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#off","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-overwrite tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":45,"kind":2048,"name":"removeAllListeners","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#removealllisteners","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-overwrite tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":46,"kind":2048,"name":"setMaxListeners","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#setmaxlisteners","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-overwrite tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":47,"kind":2048,"name":"getMaxListeners","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#getmaxlisteners","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-overwrite tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":48,"kind":2048,"name":"listeners","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#listeners","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-overwrite tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":49,"kind":2048,"name":"rawListeners","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#rawlisteners","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-overwrite tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":50,"kind":2048,"name":"eventNames","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#eventnames","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-overwrite tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":51,"kind":2048,"name":"listenerCount","url":"classes/_manageddownloaderstream_.manageddownloaderstream.html#listenercount","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-overwrite tsd-is-inherited","parent":"\"ManagedDownloaderStream\".ManagedDownloaderStream"},{"id":52,"kind":1,"name":"\"ManagedDownloader\"","url":"modules/_manageddownloader_.html","classes":"tsd-kind-external-module"},{"id":53,"kind":256,"name":"ManagedDownloaderOptions","url":"interfaces/_manageddownloader_.manageddownloaderoptions.html","classes":"tsd-kind-interface tsd-parent-kind-external-module","parent":"\"ManagedDownloader\""},{"id":54,"kind":1024,"name":"maxPartSize","url":"interfaces/_manageddownloader_.manageddownloaderoptions.html#maxpartsize","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"\"ManagedDownloader\".ManagedDownloaderOptions"},{"id":55,"kind":1024,"name":"maxConcurrency","url":"interfaces/_manageddownloader_.manageddownloaderoptions.html#maxconcurrency","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"\"ManagedDownloader\".ManagedDownloaderOptions"},{"id":56,"kind":256,"name":"GetObjectStreamInput","url":"interfaces/_manageddownloader_.getobjectstreaminput.html","classes":"tsd-kind-interface tsd-parent-kind-external-module","parent":"\"ManagedDownloader\""},{"id":57,"kind":1024,"name":"Bucket","url":"interfaces/_manageddownloader_.getobjectstreaminput.html#bucket","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"\"ManagedDownloader\".GetObjectStreamInput"},{"id":58,"kind":1024,"name":"Key","url":"interfaces/_manageddownloader_.getobjectstreaminput.html#key","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"\"ManagedDownloader\".GetObjectStreamInput"},{"id":59,"kind":1024,"name":"Range","url":"interfaces/_manageddownloader_.getobjectstreaminput.html#range","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"\"ManagedDownloader\".GetObjectStreamInput"},{"id":60,"kind":1024,"name":"PartNumber","url":"interfaces/_manageddownloader_.getobjectstreaminput.html#partnumber","classes":"tsd-kind-property tsd-parent-kind-interface","parent":"\"ManagedDownloader\".GetObjectStreamInput"},{"id":61,"kind":128,"name":"ManagedDownloader","url":"classes/_manageddownloader_.manageddownloader.html","classes":"tsd-kind-class tsd-parent-kind-external-module","parent":"\"ManagedDownloader\""},{"id":62,"kind":1024,"name":"maxPartSize","url":"classes/_manageddownloader_.manageddownloader.html#maxpartsize","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-private","parent":"\"ManagedDownloader\".ManagedDownloader"},{"id":63,"kind":1024,"name":"maxConcurrency","url":"classes/_manageddownloader_.manageddownloader.html#maxconcurrency","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-private","parent":"\"ManagedDownloader\".ManagedDownloader"},{"id":64,"kind":512,"name":"constructor","url":"classes/_manageddownloader_.manageddownloader.html#constructor","classes":"tsd-kind-constructor tsd-parent-kind-class","parent":"\"ManagedDownloader\".ManagedDownloader"},{"id":65,"kind":1024,"name":"client","url":"classes/_manageddownloader_.manageddownloader.html#client","classes":"tsd-kind-property tsd-parent-kind-class tsd-is-private","parent":"\"ManagedDownloader\".ManagedDownloader"},{"id":66,"kind":2048,"name":"getObjectStream","url":"classes/_manageddownloader_.manageddownloader.html#getobjectstream","classes":"tsd-kind-method tsd-parent-kind-class","parent":"\"ManagedDownloader\".ManagedDownloader"},{"id":67,"kind":2048,"name":"downloadByParts","url":"classes/_manageddownloader_.manageddownloader.html#downloadbyparts","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-private","parent":"\"ManagedDownloader\".ManagedDownloader"},{"id":68,"kind":2048,"name":"waitForDrainEvent","url":"classes/_manageddownloader_.manageddownloader.html#waitfordrainevent","classes":"tsd-kind-method tsd-parent-kind-class tsd-is-private","parent":"\"ManagedDownloader\".ManagedDownloader"}]}; -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | @aws/s3-managed-downloader 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
47 |
48 |
49 |
50 |
51 |
52 | 57 |

@aws/s3-managed-downloader

58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |

AWS SDK JavaScript S3 Managed Download

66 |

The JavaScript SDK Managed Download can be used for custom S3 downloads. The client can configure options for the Managed Download such as chunk size and number of concurrent transfers.

67 |

Installing

68 |

In Node.js

69 |

This package only works in Node.js versions 6+ currently. Use the npm package manager for Node.js to install the Managed Download package. Type the following command in a terminal window.

70 |
    npm install @aws/s3-managed-download
 71 | 

Documentation

72 |

You can find the full documentation for the Managed Download package here.

73 |

Configuration

74 |

You can customize the part size and number of concurrent downloads for the Managed Download by setting the maxPartSize and maxConcurrency options. The maxPartSize option controls the size in bytes of each part of the transfer. The maxConcurrency option specifies the number of parts that will be downloaded in parallel and the maximum number of parts that will be buffered in memory at any time.

75 |

Import the Managed Download package

76 |

You will need an S3 client in order to create a managed download, so import the aws-sdk along with the managed download package.

77 |

JavaScript

78 |
    const S3 = require('aws-sdk/clients/s3');
 79 |     const ManagedDownloader = require('@aws/s3-managed-download').ManagedDownloader;
 80 | 

TypeScript

81 |
    import * as S3 from 'aws-sdk/clients/s3';
 82 |     import { ManagedDownloader, GetObjectStreamInput, ManagedDownloaderOptions } from '@aws/s3-managed-download';
 83 | 

Create a Managed Download with the default part size and concurrency

84 |

Create an AWS S3 client and pass the client into the Managed Download constructor to create a Managed Download with a part size of 5MB and concurrency of 1.

85 |

JavaScript

86 |
    const s3 = new S3();
 87 |     const managedDownloader = new ManagedDownloader(s3);
 88 | 

TypeScript

89 |
    const s3:S3 = new S3();
 90 |     const managedDownloader:ManagedDownloader = new ManagedDownloader(s3);
 91 | 

Create a Managed Download with custom part size and concurrency

92 |

Create an AWS S3 client and Managed Download options. Pass the client and the options into the Managed Download constructor to create a Managed Download with a custom part size of 10MB and concurrency of 5.

93 |

JavaScript

94 |
    const s3 = new S3();
 95 |     const options = {
 96 |         maxPartSize: 10 * 1024 * 1024,
 97 |         maxConcurrency: 5
 98 |     };
 99 |     const managedDownloader = new ManagedDownloader(s3, options);
100 | 

TypeScript

101 |
    const s3:S3 = new S3();
102 |     const options:ManagedDownloaderOptons = {
103 |         maxPartSize: 10 * 1024 * 1024,
104 |         maxConcurrency: 5
105 |     };
106 |     const managedDownloader:ManagedDownloader = new ManagedDownloader(s3, options);
107 | 

Examples

108 |

Currently, the Managed Download only contains a streaming download operation which can work for an entire file or a specific range or part of a file. Here are some examples on 109 | how to use the Managed Download's getObjectStream download operation.

110 |

1. Use the Managed Download object to create a download stream for a file on S3

111 |

Get the file 'example-key' from the bucket 'example-bucket' and use the getObjectStream method to create a local file at 'example-file-path'.

112 |

JavaScript

113 |
    const S3 = require('aws-sdk/clients/s3');
114 |     const ManagedDownloader = require('@aws/s3-managed-download').ManagedDownloader;
115 |     const fs = require('fs');
116 | 
117 |     const s3 = new S3();
118 |     const managedDownloader = new ManagedDownloader(s3);
119 | 
120 |     const params = {
121 |         Bucket: 'example-bucket',
122 |         Key: 'example-key'
123 |     };
124 |     // create a write stream for a file
125 |     const writeStream = fs.createWriteStream('example-file-path');
126 | 
127 |     managedDownloader.getObjectStream(params)
128 |     .then((stream) => {
129 |         stream.pipe(writeStream);
130 |     }, (err) => {
131 |         writeStream.end();
132 |         console.error(err);
133 |     });
134 | 

TypeScript

135 |
    import * as S3 from 'aws-sdk/clients/s3';
136 |     import { ManagedDownloader, GetObjectStreamInput, ManagedDownloaderOptions } from '@aws/s3-managed-download';
137 |     import * as fs from 'fs';
138 | 
139 |     const s3:S3 = new S3();
140 |     const managedDownloader:ManagedDownloader = new ManagedDownloader(s3);
141 | 
142 |     const params:GetObjectStreamInput = {
143 |         Bucket: 'example-bucket',
144 |         Key: 'example-key'
145 |     };
146 |     // create a write stream for a file
147 |     const writeStream:fs.WriteStream = fs.createWriteStream('example-file-path');
148 | 
149 |     managedDownloader.getObjectStream(params)
150 |     .then((stream) => {
151 |         stream.pipe(writeStream);
152 |     }, (err) => {
153 |         writeStream.end();
154 |         console.error(err);
155 |     });
156 | 

2. Use the Managed Download object to create a download stream for a range of bytes of a file on S3

157 |

Get the file 'example-key' from the bucket 'example-bucket' and use the getObjectStream method to write to bytes 100-150 of a local file at 'example-file-path'.

158 |

JavaScript

159 |
    const S3 = require('aws-sdk/clients/s3');
160 |     const ManagedDownloader = require('@aws/s3-managed-download').ManagedDownloader;
161 |     const fs = require('fs');
162 | 
163 |     const s3 = new S3();
164 |     const managedDownloader = new ManagedDownloader(s3);
165 | 
166 |     const params = {
167 |         Bucket: 'example-bucket',
168 |         Key: 'example-key',
169 |         Range: '100-150'
170 |     };
171 |     // create a write stream for a file starting at byte 100
172 |     const writeStream = fs.createWriteStream('example-file-path', {start:100});
173 | 
174 |     managedDownloader.getObjectStream(params)
175 |     .then((stream) => {
176 |         stream.pipe(writeStream);
177 |     }, (err) => {
178 |         writeStream.end();
179 |         console.error(err);
180 |     });
181 | 

TypeScript

182 |
    import * as S3 from 'aws-sdk/clients/s3';
183 |     import { ManagedDownloader, GetObjectStreamInput, ManagedDownloaderOptions } from '@aws/s3-managed-download';
184 |     import * as fs from 'fs';
185 | 
186 |     const s3:S3 = new S3();
187 |     const managedDownloader:ManagedDownloader = new ManagedDownloader(s3);
188 | 
189 |     const params:GetObjectStreamInput = {
190 |         Bucket: 'example-bucket',
191 |         Key: 'example-key',
192 |         Range: '100-150'
193 |     };
194 |     // create a write stream for a file starting at byte 100
195 |     const writeStream:fs.WriteStream = fs.createWriteStream('example-file-path', {start:100});
196 | 
197 |     managedDownloader.getObjectStream(params)
198 |     .then((stream) => {
199 |         stream.pipe(writeStream);
200 |     }, (err) => {
201 |         writeStream.end();
202 |         console.error(err);
203 |     });
204 | 

Opening Issues

205 |

If you find any bugs or want to request a new feature, please first check the existing issues. If there is not already an issue, then open a new issue. If the issue is regarding a bug, please include the stack trace and a method to recreate it.

206 |

License

207 |

This library is licensed under the Apache 2.0 License.

208 |
209 |
210 | 229 |
230 |
231 |
232 |
233 |

Legend

234 |
235 |
    236 |
  • Module
  • 237 |
  • Object literal
  • 238 |
  • Variable
  • 239 |
  • Function
  • 240 |
  • Function with type parameter
  • 241 |
  • Index signature
  • 242 |
  • Type alias
  • 243 |
244 |
    245 |
  • Enumeration
  • 246 |
  • Enumeration member
  • 247 |
  • Property
  • 248 |
  • Method
  • 249 |
250 |
    251 |
  • Interface
  • 252 |
  • Interface with type parameter
  • 253 |
  • Constructor
  • 254 |
  • Property
  • 255 |
  • Method
  • 256 |
  • Index signature
  • 257 |
258 |
    259 |
  • Class
  • 260 |
  • Class with type parameter
  • 261 |
  • Constructor
  • 262 |
  • Property
  • 263 |
  • Method
  • 264 |
  • Accessor
  • 265 |
  • Index signature
  • 266 |
267 |
    268 |
  • Inherited constructor
  • 269 |
  • Inherited property
  • 270 |
  • Inherited method
  • 271 |
  • Inherited accessor
  • 272 |
273 |
    274 |
  • Protected property
  • 275 |
  • Protected method
  • 276 |
  • Protected accessor
  • 277 |
278 |
    279 |
  • Private property
  • 280 |
  • Private method
  • 281 |
  • Private accessor
  • 282 |
283 |
    284 |
  • Static property
  • 285 |
  • Static method
  • 286 |
287 |
288 |
289 |
290 |
291 |

Generated using TypeDoc

292 |
293 |
294 | 295 | 296 | 297 | -------------------------------------------------------------------------------- /src/test/unit/ManagedDownloader.spec.ts: -------------------------------------------------------------------------------- 1 | import { ManagedDownloader, ManagedDownloaderOptions, GetObjectStreamInput } from '../../ManagedDownloader'; 2 | import * as S3 from 'aws-sdk/clients/s3'; 3 | import { getRangeOfPart } from '../../getRangeOfPart'; 4 | import { getInformationFromRange } from '../../getInformationFromRange'; 5 | import { PassThrough } from 'stream'; 6 | 7 | const getObjectMockOutput = (contentRange:string, body:Buffer = new Buffer('')) => { 8 | return { 9 | promise: () => Promise.resolve({ 10 | Body: body, 11 | ContentRange:contentRange 12 | }) 13 | } 14 | } 15 | 16 | const getObjectMockError = () => { 17 | return { 18 | promise: () => Promise.reject(new Error()) 19 | } 20 | } 21 | 22 | describe('ManagedDownloader constructor', () => { 23 | 24 | const Mock = jest.fn(() => ({ 25 | getObject: jest.fn(() => 26 | getObjectMockOutput('bytes=0-0/0') 27 | ) 28 | }) 29 | ); 30 | const mock = new Mock(); 31 | 32 | it('will create an options object with defaults set if no options are provided', () => { 33 | const managedDownloader:ManagedDownloader = new ManagedDownloader(mock); 34 | expect((managedDownloader as any).maxPartSize) 35 | .toBe(1024*1024*5); 36 | }); 37 | 38 | it('will set options to provided options', () => { 39 | const options:ManagedDownloaderOptions = { 40 | maxPartSize:1024*1024*4 41 | } 42 | const managedDownloader:ManagedDownloader = new ManagedDownloader(mock, options); 43 | expect((managedDownloader as any).maxPartSize) 44 | .toBe(1024*1024*4); 45 | }); 46 | 47 | it('will set options to provided options and defaults if not all options are provided', () => { 48 | const options:ManagedDownloaderOptions = {} 49 | const managedDownloader:ManagedDownloader = new ManagedDownloader(mock, options); 50 | expect((managedDownloader as any).maxPartSize) 51 | .toBe(1024*1024*5); 52 | }); 53 | 54 | it('will throw an error if the client is not provided', () => { 55 | //@ts-ignore 56 | expect(() => new ManagedDownloader()).toThrow(Error('S3 client must be provided')); 57 | }); 58 | 59 | it('will throw an error if maximum part size is invalid', () => { 60 | const options:ManagedDownloaderOptions = {maxPartSize: -1}; 61 | //@ts-ignore 62 | expect(() => new ManagedDownloader(mock, options)).toThrow(Error('Maximum part size must be a positive number')); 63 | }); 64 | 65 | it('will throw an error if maximum concurrency is invalid', () => { 66 | const options:ManagedDownloaderOptions = {maxConcurrency: -1}; 67 | //@ts-ignore 68 | expect(() => new ManagedDownloader(mock, options)).toThrow(Error('Maximum concurrency must be a positive number')); 69 | }); 70 | 71 | }); 72 | 73 | describe('getObjectStream', () => { 74 | 75 | it('will properly calculate the range of bytes based' 76 | + ' on the max part size given a starting part and file length', () => { 77 | expect.assertions(100); 78 | const partSize = 1024*10; 79 | for (let i = 0; i < 10; i++) { 80 | for (let j = 0; j < 10; j++) { 81 | // random length from 0 to 1000 82 | let length = Math.floor(Math.random() * 1000); 83 | expect(getRangeOfPart(i, length, partSize)) 84 | .toBe('bytes=' + i * partSize 85 | + '-' + Math.min(((i + 1) * partSize) - 1, length - 1)); 86 | } 87 | } 88 | }); 89 | 90 | it(`will properly calculate the range of bytes if a byte offset is 91 | provided`, () => { 92 | expect.assertions(100); 93 | const partSize = 1024*10; 94 | for (let i = 0; i < 10; i++) { 95 | for (let j = 0; j < 10; j++) { 96 | // random length from 0 to 1000 97 | let length = Math.floor(Math.random() * 1000); 98 | let byteOffset = Math.floor(Math.random() * 100); 99 | expect(getRangeOfPart(i, length, partSize, byteOffset)) 100 | .toBe('bytes=' + ((i * partSize) + byteOffset) 101 | + '-' + Math.min(((i + 1) * partSize) - 1 + byteOffset, 102 | length - 1 + byteOffset)); 103 | } 104 | } 105 | }) 106 | 107 | it(`will properly get information from a user provided range and 108 | throw errors if it is in the wrong format`, () => { 109 | expect(getInformationFromRange('bytes=7-19').startByte).toBe(7); 110 | expect(getInformationFromRange('bytes=191-1241').endByte).toBe(1241); 111 | expect(getInformationFromRange('bytes=100-900').length).toBe(800); 112 | expect(() => getInformationFromRange('sruti')).toThrow(Error); 113 | expect(() => getInformationFromRange('bytes=199:999')).toThrow(Error); 114 | }) 115 | 116 | it('will return a Promise', () => { 117 | const source:GetObjectStreamInput = { 118 | Key:'doesntexist.txt', 119 | Bucket:'doesntexist' 120 | }; 121 | const Mock = jest.fn(() => ({ 122 | getObject: jest.fn(() => 123 | getObjectMockOutput('bytes=1-10/10') 124 | ) 125 | }) 126 | ); 127 | const mock = new Mock(); 128 | const managedDownloader:ManagedDownloader = new ManagedDownloader(mock); 129 | expect(managedDownloader.getObjectStream(source)).toBeInstanceOf(Promise); 130 | }); 131 | 132 | it('will call getObject with the first range being bytes=0-[maxPartSize-1]', async() => { 133 | const source:GetObjectStreamInput = { 134 | Key:'doesntexist.txt', 135 | Bucket:'doesntexist', 136 | }; 137 | const Mock = jest.fn(() => ({ 138 | getObject: jest.fn(() => 139 | getObjectMockOutput('bytes=0-9/100')) 140 | }) 141 | ); 142 | const mock = new Mock(); 143 | const managedDownloader = new ManagedDownloader(mock, {maxPartSize:10}); 144 | managedDownloader.getObjectStream(source); 145 | expect(mock.getObject).toHaveBeenCalledWith( 146 | { 147 | Key:source.Key, 148 | Bucket:source.Bucket, 149 | Range:'bytes=0-9' 150 | } 151 | ); 152 | }); 153 | 154 | it('will properly handle errors thrown by getObject', async() => { 155 | const source:GetObjectStreamInput = { 156 | Key:'doesntexist.txt', 157 | Bucket:'doesntexist' 158 | }; 159 | const Mock = jest.fn(() => ({ 160 | getObject: jest.fn(() => 161 | ( 162 | { 163 | promise: () => 164 | Promise.reject(new Error('Error!')) 165 | } 166 | ) 167 | ) 168 | }) 169 | ); 170 | let downloadError:Error|undefined; 171 | const mock = new Mock(); 172 | const managedDownloader:ManagedDownloader = new ManagedDownloader(mock); 173 | try { 174 | await managedDownloader.getObjectStream(source); 175 | } catch(err) { 176 | downloadError = err; 177 | } 178 | expect(downloadError).toBeInstanceOf(Error); 179 | }); 180 | 181 | it('will not accept a source in the wrong format', async() => { 182 | //@ts-ignore 183 | const source:GetObjectStreamInput = { 184 | //@ts-ignore 185 | Invalid:'doesntexist.txt', 186 | Source:'doesntexist' 187 | }; 188 | const Mock = jest.fn(() => ({ 189 | getObject: jest.fn(() => 190 | ( 191 | { 192 | promise: () => 193 | Promise.reject(new Error('Error!')) 194 | } 195 | ) 196 | ) 197 | }) 198 | ); 199 | let downloadError:Error|undefined; 200 | const mock = new Mock(); 201 | const managedDownloader:ManagedDownloader = new ManagedDownloader(mock); 202 | try { 203 | await managedDownloader.getObjectStream(source); 204 | } catch(err) { 205 | downloadError = err; 206 | } 207 | expect(downloadError).toBeInstanceOf(Error); 208 | }); 209 | 210 | describe('A client can provide a custom range to download', () => { 211 | 212 | it(`will call the downloadByParts function once with the right parameters when 213 | a client provides the range and it is greater than maxPartSize`, async() => { 214 | const source:GetObjectStreamInput = { 215 | Key:'doesntexist.txt', 216 | Bucket:'doesntexist', 217 | Range: 'bytes=100-800' 218 | }; 219 | const Mock = jest.fn(() => ({ 220 | getObject: jest.fn((source) => 221 | getObjectMockOutput('bytes=0-9/100') 222 | ) 223 | }) 224 | ); 225 | const mock = new Mock(); 226 | const managedDownloader = new ManagedDownloader(mock, {maxPartSize:10}); 227 | const spy = jest.spyOn((managedDownloader as any), 'downloadByParts'); 228 | expect.assertions(2); 229 | await managedDownloader.getObjectStream(source); 230 | expect(spy).toHaveBeenCalledTimes(1); 231 | expect(spy).toHaveBeenCalledWith( 232 | expect.any(Object), 233 | expect.any(PassThrough), 234 | 700, 235 | 100, 236 | 1 237 | ); 238 | }); 239 | 240 | it(`will not call downloadByParts if the provided range is less than or equal to maxPartSize`, async() => { 241 | const source:GetObjectStreamInput = { 242 | Key:'doesntexist.txt', 243 | Bucket:'doesntexist', 244 | Range: 'bytes=10-19' 245 | }; 246 | const Mock = jest.fn(() => ({ 247 | getObject: jest.fn((source) => 248 | getObjectMockOutput('bytes=10-19/100') 249 | ) 250 | }) 251 | ); 252 | const mock = new Mock(); 253 | const managedDownloader = new ManagedDownloader(mock, {maxPartSize:10}); 254 | const spy = jest.spyOn((managedDownloader as any), 'downloadByParts'); 255 | expect.assertions(1); 256 | await managedDownloader.getObjectStream(source); 257 | expect(spy).toHaveBeenCalledTimes(0); 258 | }); 259 | }); 260 | 261 | describe('A client can provide a part number', () => { 262 | it('will not call downloadByParts if the part number is provided', async() => { 263 | const source:GetObjectStreamInput = { 264 | Key:'doesntexist.txt', 265 | Bucket:'doesntexist', 266 | PartNumber: 2 267 | }; 268 | const Mock = jest.fn(() => ({ 269 | getObject: jest.fn((source) => 270 | getObjectMockOutput('bytes=0-9/100') 271 | ) 272 | })); 273 | const mock = new Mock(); 274 | const managedDownloader = new ManagedDownloader(mock); 275 | const spy = jest.spyOn((managedDownloader as any), 'downloadByParts'); 276 | expect.assertions(1); 277 | await managedDownloader.getObjectStream(source); 278 | expect(spy).toHaveBeenCalledTimes(0); 279 | }); 280 | 281 | it('will call getObject with no range, only PartNumber', async() => { 282 | const source:GetObjectStreamInput = { 283 | Key:'doesntexist.txt', 284 | Bucket:'doesntexist', 285 | PartNumber: 1 286 | }; 287 | const Mock = jest.fn(() => ({ 288 | getObject: jest.fn(() => 289 | getObjectMockOutput('bytes=0-9/100')) 290 | }) 291 | ); 292 | const mock = new Mock(); 293 | const managedDownloader = new ManagedDownloader(mock, {maxPartSize:10}); 294 | managedDownloader.getObjectStream(source); 295 | expect(mock.getObject).toHaveBeenCalledWith( 296 | { 297 | Key:source.Key, 298 | Bucket:source.Bucket, 299 | PartNumber: 1 300 | } 301 | ); 302 | }); 303 | }); 304 | }); 305 | 306 | describe('downloadByParts', () => { 307 | 308 | let originalTimeout:number; 309 | 310 | beforeAll(() => { 311 | originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; 312 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; 313 | }) 314 | 315 | afterAll(() => { 316 | jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; 317 | }) 318 | 319 | it('will call getObject [numParts = length/maxPartSize] number of times with the proper ranges', async() => { 320 | const source:GetObjectStreamInput = { 321 | Key:'doesntexist.txt', 322 | Bucket:'doesntexist', 323 | Range: 'bytes=10-19' 324 | }; 325 | const stream = new PassThrough(); 326 | const Mock = jest.fn(() => ({ 327 | getObject: jest.fn(() => 328 | getObjectMockOutput('bytes=0-9/100') 329 | ) 330 | }) 331 | ); 332 | const mock = new Mock(); 333 | const managedDownloader = new ManagedDownloader(mock, {maxPartSize:10}); 334 | expect.assertions(2); 335 | await (managedDownloader as any).downloadByParts(source, stream, 100); 336 | expect(mock.getObject).toHaveBeenCalledTimes(10); 337 | expect(mock.getObject).toHaveBeenLastCalledWith( 338 | { 339 | Key:source.Key, 340 | Bucket:source.Bucket, 341 | Range:'bytes=90-99' 342 | } 343 | ); 344 | }); 345 | 346 | it('will properly handle errors and stop executing after an error', async() => { 347 | const source:GetObjectStreamInput = { 348 | Key:'doesntexist.txt', 349 | Bucket:'doesntexist', 350 | Range: 'bytes=10-19' 351 | }; 352 | const stream = new PassThrough(); 353 | const Mock = jest.fn(() => ({ 354 | getObject: jest.fn() 355 | .mockReturnValueOnce( 356 | getObjectMockOutput('bytes=0-9/100') 357 | ) 358 | .mockReturnValueOnce( 359 | getObjectMockOutput('bytes=10-19/100') 360 | ).mockReturnValue(getObjectMockError()) 361 | })); 362 | const mock = new Mock(); 363 | const managedDownloader = new ManagedDownloader(mock, {maxPartSize:10}); 364 | const func = new Promise((resolve, reject) => { 365 | stream.on('error', (err) => { 366 | reject(err); 367 | }); 368 | (managedDownloader as any).downloadByParts(source, stream, 100) 369 | }); 370 | await expect(func).rejects.toBeInstanceOf(Error); 371 | expect(mock.getObject).toHaveBeenCalledTimes(3); 372 | }); 373 | 374 | it('will wait until a drain event is fired on the stream before continuing execution', async() => { 375 | 376 | const source:GetObjectStreamInput = { 377 | Key:'doesntexist.txt', 378 | Bucket:'doesntexist', 379 | Range: 'bytes=10-19' 380 | }; 381 | const Mock = jest.fn(() => ({ 382 | getObject: jest.fn((source) => 383 | getObjectMockOutput('bytes=10-19/50') 384 | ) 385 | }) 386 | ); 387 | 388 | const mock = new Mock(); 389 | const streamMock = new PassThrough(); 390 | const falseIndex = Math.floor((Math.random() * 3)); 391 | /** 392 | * Mock the PassThrough.write() function to return false 393 | * once in order to test if waitForDrainEvent works 394 | * properly and execution continues as usual afterwards. 395 | */ 396 | streamMock.write = jest.fn() 397 | .mockReturnValueOnce(!(falseIndex == 0)) 398 | .mockReturnValueOnce(!(falseIndex == 1)) 399 | .mockReturnValueOnce(!(falseIndex == 2)) 400 | .mockReturnValueOnce(!(falseIndex == 3)) 401 | .mockReturnValue(true); 402 | streamMock.end = jest.fn(() => { 403 | streamMock.emit('end'); 404 | }); 405 | const managedDownloader = new ManagedDownloader(mock, {maxPartSize:10}); 406 | const func = new Promise((resolve, reject) => { 407 | streamMock 408 | .on('error', (err) => { 409 | reject(err); 410 | }) 411 | .on('end', () => { 412 | resolve(true); 413 | }); 414 | (managedDownloader as any).downloadByParts(source, streamMock, 50); 415 | setTimeout(() => { 416 | streamMock.emit('drain'); 417 | }, 3000); 418 | }); 419 | await expect(func).resolves.toBe(true); 420 | expect(mock.getObject).toHaveBeenCalledTimes(5); 421 | }); 422 | 423 | describe('A client provides a custom range to download', () => { 424 | it('Will call getObject [numParts = length/maxPartSize] number of times ' 425 | + 'with the proper ranges when the client provides a range', async() => { 426 | 427 | // client provided range: bytes=199-799 428 | // the first getObject call will handle 199-248 before downloadByParts 429 | // therefore downloadByParts starts on the range 249-298 430 | const source:GetObjectStreamInput = { 431 | Key:'doesntexist.txt', 432 | Bucket:'doesntexist', 433 | Range: 'bytes=249-298' 434 | }; 435 | const stream = new PassThrough(); 436 | const Mock = jest.fn(() => ({ 437 | getObject: jest.fn((source) => 438 | getObjectMockOutput('bytes=249-298/800') 439 | ) 440 | }) 441 | ); 442 | const mock = new Mock(); 443 | const managedDownloader = new ManagedDownloader(mock, {maxPartSize:50}); 444 | expect.assertions(2); 445 | // last 2 args are length:600 (799-199) and byteOffset:199 (start byte) 446 | await (managedDownloader as any).downloadByParts(source, stream, 600, 199); 447 | expect(mock.getObject).toHaveBeenCalledTimes(12); 448 | expect(mock.getObject).toHaveBeenLastCalledWith( 449 | { 450 | Key:source.Key, 451 | Bucket:source.Bucket, 452 | Range:'bytes=749-798' 453 | } 454 | ); 455 | }); 456 | }); 457 | 458 | describe('A client provides a custom concurrency > 1', () => { 459 | for (let x = 2; x < 10; x++) { 460 | it('will write parts to the stream in the correct order', async() => { 461 | const source:GetObjectStreamInput = { 462 | Key:'doesntexist.txt', 463 | Bucket:'doesntexist', 464 | Range: 'bytes=0-9' 465 | }; 466 | const getObjectBodyArr = [ 467 | new Buffer('A'), new Buffer('B'), 468 | new Buffer('C'), new Buffer('D'), 469 | new Buffer('E') 470 | ] 471 | const stream = new PassThrough(); 472 | 473 | const Mock = jest.fn(() => ({ 474 | getObject: jest.fn() 475 | .mockReturnValueOnce( 476 | getObjectMockOutput('bytes=0-9/100', getObjectBodyArr[0]) 477 | ) 478 | .mockReturnValueOnce( 479 | getObjectMockOutput('bytes=10-19/100', getObjectBodyArr[1]) 480 | ) 481 | .mockReturnValueOnce( 482 | getObjectMockOutput('bytes=10-19/100', getObjectBodyArr[2]) 483 | ) 484 | .mockReturnValueOnce( 485 | getObjectMockOutput('bytes=10-19/100', getObjectBodyArr[3]) 486 | ) 487 | .mockReturnValueOnce( 488 | getObjectMockOutput('bytes=10-19/100', getObjectBodyArr[4]) 489 | ) 490 | })); 491 | const mock = new Mock(); 492 | const managedDownloader = new ManagedDownloader(mock, {maxPartSize:10, maxConcurrency:x}); 493 | let getObjectBodyIndex = 0; 494 | const func = new Promise((resolve, reject) => { 495 | stream 496 | .on('data', (body) => { 497 | if (body === getObjectBodyArr[getObjectBodyIndex]) { 498 | getObjectBodyIndex++; 499 | } else { 500 | console.log(body); 501 | reject(false); 502 | } 503 | }) 504 | .on('end', () => { 505 | resolve(true); 506 | }) 507 | .on('error', (err) => { 508 | reject(err); 509 | }); 510 | (managedDownloader as any).downloadByParts(source, stream, 50) 511 | }); 512 | await expect(func).resolves.toBe(true); 513 | expect(mock.getObject).toHaveBeenCalledTimes(5); 514 | }); 515 | 516 | it('will call getObject [numParts = length/maxPartSize] number of times with the proper ranges', async() => { 517 | const source:GetObjectStreamInput = { 518 | Key:'doesntexist.txt', 519 | Bucket:'doesntexist', 520 | Range: 'bytes=10-19' 521 | }; 522 | const stream = new PassThrough(); 523 | const Mock = jest.fn(() => ({ 524 | getObject: jest.fn(() => 525 | getObjectMockOutput('bytes=0-9/100') 526 | ) 527 | }) 528 | ); 529 | const mock = new Mock(); 530 | const managedDownloader = new ManagedDownloader(mock, {maxPartSize:10, maxConcurrency:x}); 531 | expect.assertions(2); 532 | await (managedDownloader as any).downloadByParts(source, stream, 100); 533 | expect(mock.getObject).toHaveBeenCalledTimes(10); 534 | expect(mock.getObject).toHaveBeenLastCalledWith( 535 | { 536 | Key:source.Key, 537 | Bucket:source.Bucket, 538 | Range:'bytes=90-99' 539 | } 540 | ); 541 | }); 542 | 543 | it('will properly handle errors and stop executing after an error', async() => { 544 | const source:GetObjectStreamInput = { 545 | Key:'doesntexist.txt', 546 | Bucket:'doesntexist', 547 | Range: 'bytes=10-19' 548 | }; 549 | const stream = new PassThrough(); 550 | const Mock = jest.fn(() => ({ 551 | getObject: jest.fn() 552 | .mockReturnValueOnce( 553 | getObjectMockOutput('bytes=0-9/100', new Buffer('0')) 554 | ).mockReturnValueOnce( 555 | getObjectMockOutput('bytes=10-19/100', new Buffer('0')) 556 | ).mockReturnValueOnce( 557 | getObjectMockError() 558 | ).mockReturnValueOnce( 559 | getObjectMockOutput('bytes=30-39/100', new Buffer('0')) 560 | ).mockReturnValueOnce( 561 | getObjectMockOutput('bytes=40-49/100', new Buffer('0')) 562 | ).mockReturnValueOnce( 563 | getObjectMockOutput('bytes=50-59/100', new Buffer('0')) 564 | ).mockReturnValueOnce( 565 | getObjectMockOutput('bytes=60-69/100', new Buffer('0')) 566 | ).mockReturnValueOnce( 567 | getObjectMockOutput('bytes=70-79/100', new Buffer('0')) 568 | ).mockReturnValueOnce( 569 | getObjectMockOutput('bytes=80-89/100', new Buffer('0')) 570 | ).mockReturnValueOnce( 571 | getObjectMockOutput('bytes=90-99/100', new Buffer('0')) 572 | ) 573 | 574 | })); 575 | const mock = new Mock(); 576 | const managedDownloader = new ManagedDownloader(mock, {maxPartSize:10, maxConcurrency:x}); 577 | const func = new Promise((resolve, reject) => { 578 | let getObjectCounter = 0; 579 | stream 580 | .on('data', (chunk) => { 581 | getObjectCounter++; 582 | }) 583 | .on('error', (err) => { 584 | reject(getObjectCounter); 585 | }); 586 | (managedDownloader as any).downloadByParts(source, stream, 100) 587 | }); 588 | // regardless of the concurrency and the number of 589 | // getObject requests it makes, it should stop writing after 2 590 | // because then it will emit an error 591 | await expect(func).rejects.toBe(2); 592 | }); 593 | } 594 | }); 595 | }); 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | -------------------------------------------------------------------------------- /docs/classes/_manageddownloader_.manageddownloader.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | ManagedDownloader | @aws/s3-managed-downloader 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | 27 |
28 |
29 | Options 30 |
31 |
32 | All 33 |
    34 |
  • Public
  • 35 |
  • Public/Protected
  • 36 |
  • All
  • 37 |
38 |
39 | 40 | 41 | 42 | 43 |
44 |
45 | Menu 46 |
47 |
48 |
49 |
50 |
51 |
52 | 63 |

Class ManagedDownloader

64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |

The ManagedDownloader class handles custom S3 downloads. 74 | A client can set options for the download such as 75 | the part size and the concurrency.

76 |
77 |
78 |
79 |
80 |

Hierarchy

81 |
    82 |
  • 83 | ManagedDownloader 84 |
  • 85 |
86 |
87 |
88 |

Index

89 |
90 |
91 |
92 |

Constructors

93 | 96 |
97 |
98 |

Properties

99 | 104 |
105 |
106 |

Methods

107 | 112 |
113 |
114 |
115 |
116 |
117 |

Constructors

118 |
119 | 120 |

constructor

121 | 124 |
    125 |
  • 126 | 131 |
    132 |
    133 |

    Create a ManagedDownloader object with an 134 | s3 client and custom options if provided.

    135 |
    136 |
    137 |
    throws
    138 |

    Error if the client is not provided.

    139 |
    140 |
    throws
    141 |

    Error if maxPartSize is not a positive number.

    142 |
    143 |
    throws
    144 |

    Error if concurrency is not a positive number.

    145 |
    146 |
    147 |
    148 |

    Parameters

    149 |
      150 |
    • 151 |
      client: S3
      152 |
      153 |
      154 |

      an S3 client to make requests.

      155 |
      156 |
      157 |
    • 158 |
    • 159 |
      Default value options: ManagedDownloaderOptions = {}
      160 |
      161 |
      162 |

      configuration for the managed download.

      163 |
      164 |
      165 |
    • 166 |
    167 |

    Returns ManagedDownloader

    168 |
  • 169 |
170 |
171 |
172 |
173 |

Properties

174 |
175 | 176 |

Private client

177 |
client: S3
178 | 183 |
184 |
185 |

an S3 client to make requests.

186 |
187 |
188 |
189 |
190 | 191 |

Private maxConcurrency

192 |
maxConcurrency: number
193 | 198 |
199 |
200 | 201 |

Private maxPartSize

202 |
maxPartSize: number
203 | 208 |
209 |
210 |
param
211 |

max part size of download in bytes.

212 |
213 |
param
214 |

maximum parallel downloads.

215 |
216 |
217 |
218 |
219 |
220 |
221 |

Methods

222 |
223 | 224 |

Private downloadByParts

225 | 228 |
    229 |
  • 230 | 235 |
    236 |
    237 |

    Download the individual parts of the file 238 | and write them to the stream sequentially. 239 | Download n = this.maxConcurrency parts 240 | in parallel and store parts which 241 | aren't the next part in memory until the 242 | stream is ready to read it.

    243 |
    244 |
    245 |

    Parameters

    246 |
      247 |
    • 248 |
      params: GetObjectStreamInput
      249 |
      250 |

      params object contains the bucket 251 | and key.

      252 |
      253 |
    • 254 |
    • 255 |
      destinationStream: ManagedDownloaderStream
      256 |
      257 |

      the ManagedDownloaderStream stream 258 | transferring the parts of a file.

      259 |
      260 |
    • 261 |
    • 262 |
      contentLength: number
      263 |
      264 |

      the length of the file.

      265 |
      266 |
    • 267 |
    • 268 |
      Default value byteOffset: number = 0
      269 |
      270 |

      if available, the amount to add to start 271 | and end byte when calculating the range.

      272 |
      273 |
    • 274 |
    • 275 |
      Default value startingPart: number = 0
      276 |
      277 |

      the part number to start downloading from.

      278 |
      279 |
    • 280 |
    281 |

    Returns Promise<void>

    282 |
  • 283 |
284 |
285 |
286 | 287 |

getObjectStream

288 | 291 |
    292 |
  • 293 | 298 |
    299 |
    300 |

    Get a download stream for a file stored in s3. 301 | Download the first part of size [maxPartSize] to get 302 | the content length and set headers for the stream. 303 | If the file length is less than [maxPartSize], or the 304 | client provides a part number, then return the stream. 305 | If not, call downloadByParts to split the rest of the file into 306 | parts of size [maxPartSize] and download [maxConcurrency] parts 307 | in parallel.

    308 |
    309 |
    310 |
    throws
    311 |

    Error if params is in the incorrect format.

    312 |
    313 |
    314 |
    315 |

    Parameters

    316 |
      317 |
    • 318 |
      params: GetObjectStreamInput
      319 |
      320 |

      an object in the format 321 | { 322 | Bucket:string, 323 | Key:string, 324 | Range(optional):string, 325 | PartNumber(optional):number 326 | }.

      327 |
      328 |
    • 329 |
    330 |

    Returns Promise<ManagedDownloaderStream>

    331 |

    ManagedDownloaderStream the PassThrough stream to read 332 | file data from.

    333 |
  • 334 |
335 |
336 |
337 | 338 |

Private waitForDrainEvent

339 | 342 |
    343 |
  • 344 | 349 |
    350 |
    351 |

    wait for a stream to emit a 352 | drain event when it is ready to 353 | receive data again.

    354 |
    355 |
    356 |

    Parameters

    357 |
      358 |
    • 359 |
      destinationStream: ManagedDownloaderStream
      360 |
      361 |

      the stream 362 | that is being watched for the drain 363 | event.

      364 |
      365 |
    • 366 |
    367 |

    Returns Promise<void>

    368 |
  • 369 |
370 |
371 |
372 |
373 | 428 |
429 |
430 |
431 |
432 |

Legend

433 |
434 |
    435 |
  • Module
  • 436 |
  • Object literal
  • 437 |
  • Variable
  • 438 |
  • Function
  • 439 |
  • Function with type parameter
  • 440 |
  • Index signature
  • 441 |
  • Type alias
  • 442 |
443 |
    444 |
  • Enumeration
  • 445 |
  • Enumeration member
  • 446 |
  • Property
  • 447 |
  • Method
  • 448 |
449 |
    450 |
  • Interface
  • 451 |
  • Interface with type parameter
  • 452 |
  • Constructor
  • 453 |
  • Property
  • 454 |
  • Method
  • 455 |
  • Index signature
  • 456 |
457 |
    458 |
  • Class
  • 459 |
  • Class with type parameter
  • 460 |
  • Constructor
  • 461 |
  • Property
  • 462 |
  • Method
  • 463 |
  • Accessor
  • 464 |
  • Index signature
  • 465 |
466 |
    467 |
  • Inherited constructor
  • 468 |
  • Inherited property
  • 469 |
  • Inherited method
  • 470 |
  • Inherited accessor
  • 471 |
472 |
    473 |
  • Protected property
  • 474 |
  • Protected method
  • 475 |
  • Protected accessor
  • 476 |
477 |
    478 |
  • Private property
  • 479 |
  • Private method
  • 480 |
  • Private accessor
  • 481 |
482 |
    483 |
  • Static property
  • 484 |
  • Static method
  • 485 |
486 |
487 |
488 |
489 |
490 |

Generated using TypeDoc

491 |
492 |
493 | 494 | 495 | 496 | -------------------------------------------------------------------------------- /docs/assets/css/main.css.map: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "mappings": "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", 4 | "sources": ["../../../../src/default/assets/css/vendors/_normalize.sass","../../../../src/default/assets/css/vendors/_highlight.js.sass","../../../../src/default/assets/css/setup/_mixins.sass","../../../../src/default/assets/css/setup/_grid.sass","../../../../src/default/assets/css/setup/_icons.scss","../../../../src/default/assets/css/setup/_animations.sass","../../../../src/default/assets/css/setup/_typography.sass","../../../../src/default/assets/css/_constants.sass","../../../../src/default/assets/css/layouts/_default.sass","../../../../src/default/assets/css/layouts/_minimal.sass","../../../../src/default/assets/css/elements/_comment.sass","../../../../src/default/assets/css/elements/_filter.sass","../../../../src/default/assets/css/elements/_footer.sass","../../../../src/default/assets/css/elements/_hierarchy.sass","../../../../src/default/assets/css/elements/_index.sass","../../../../src/default/assets/css/elements/_member.sass","../../../../src/default/assets/css/elements/_navigation.sass","../../../../src/default/assets/css/elements/_panel.sass","../../../../src/default/assets/css/elements/_search.sass","../../../../src/default/assets/css/elements/_signatures.sass","../../../../src/default/assets/css/elements/_sources.sass","../../../../src/default/assets/css/elements/_toolbar.sass","../../../../src/default/assets/css/elements/_images.sass"], 5 | "names": [], 6 | "file": "main.css" 7 | } 8 | --------------------------------------------------------------------------------