UNPKG

@d3oxy/s3-pilot

Version:

A TypeScript wrapper for AWS S3 with support for multiple clients, buckets, and secure file downloads.

247 lines (188 loc) 7.24 kB
# S3Pilot S3Pilot is a TypeScript library that abstracts AWS S3 operations, making it easier to interact with S3 buckets and objects. It provides a cleaner API to manage file uploads, deletions, signed URL generation, and secure file downloads. ## Features - Multi-client support with isolated configurations. - Easily upload, delete, rename, and manage files in S3 buckets. - Generate signed URLs for private access to objects. - **NEW**: Secure file download methods to avoid CORS issues. - **NEW**: Enhanced signed URLs with download-specific headers. - **NEW**: Streaming support for large files. - Validation for bucket access and file extensions. - Supports custom key prefixes and folders. ## Installation ```bash # Using pnpm pnpm install @d3oxy/s3-pilot # Using npm npm install @d3oxy/s3-pilot # Using bun bun add @d3oxy/s3-pilot # Using yarn yarn add @d3oxy/s3-pilot ``` ## Usage ### Initialize S3Pilot First, import the `S3Pilot` class into your TypeScript project: ```typescript import { S3ClientSettings, S3ClientsSetup, S3Pilot } from "@d3oxy/s3-pilot"; ``` ### Configuration Then create a new instance of `S3Pilot` with the desired S3 clients and their configurations: ```typescript const s3Pilot = new S3Pilot< S3ClientsSetup<{ client1: S3ClientSettings<"bucket-A1" | "bucketA2">; client2: S3ClientSettings<"bucket-B1" | "bucketB2">; }> >({ client1: { region: "region", accessKeyId: "AWS_ACCESS_KEY_ID", secretAccessKey: "AWS_SECRET_ACCESS_KEY", buckets: ["bucket-A1", "bucketA2"], keyPrefix: process.env("NODE_ENV") === "development" ? "dev" : undefined, }, client2: { region: "region", accessKeyId: "AWS_ACCESS_KEY_ID", secretAccessKey: "AWS_SECRET_ACCESS_KEY", buckets: ["bucket-B1", "bucketB2"], keyPrefix: process.env("NODE_ENV") === "development" ? "dev" : undefined, }, }); ``` ### Upload Files To upload files to an S3 bucket, use the `uploadFile` method: ```typescript (async () => { const response = await s3Pilot.uploadFile("client1", "bucket-A1", { filename: "example.jpg", file: Buffer.from("Your file data"), contentType: "image/jpeg", }); console.log("Uploaded File URL:", response.url); })(); ``` ### Download Files #### Direct File Download (Recommended for Server-Side) Use the `getFile` method to download file content as a Buffer. This is ideal for server-side processing or when you need to stream files through your API server to avoid CORS issues: ```typescript (async () => { const fileResponse = await s3Pilot.getFile("client1", "bucket-A1", { key: "example.jpg", }); console.log("File content:", fileResponse.buffer); console.log("Content type:", fileResponse.contentType); console.log("File size:", fileResponse.contentLength); // Use in your API response // res.setHeader('Content-Type', fileResponse.contentType); // res.setHeader('Content-Disposition', 'attachment; filename="example.jpg"'); // res.send(fileResponse.buffer); })(); ``` #### Streaming Large Files For large files, use the `getFileStream` method to stream content efficiently: ```typescript (async () => { const streamResponse = await s3Pilot.getFileStream("client1", "bucket-A1", { key: "large-file.zip", }); console.log("Content type:", streamResponse.contentType); console.log("File size:", streamResponse.contentLength); // Pipe the stream to your response // streamResponse.stream.pipe(res); })(); ``` ### Generate Signed URLs #### Basic Signed URL To generate signed URLs for private access to S3 objects: ```typescript (async () => { const signedUrl = await s3Pilot.generateSignedUrl("client1", "bucket-A1", { key: "example.jpg", expiresIn: 3600, // URL valid for 1 hour }); console.log("Signed URL:", signedUrl); })(); ``` #### Enhanced Signed URL with Download Headers Generate signed URLs with download-specific headers to force browser download behavior and avoid CORS issues: ```typescript (async () => { const signedUrl = await s3Pilot.generateSignedUrl("client1", "bucket-A1", { key: "document.pdf", expiresIn: 3600, // 1 hour responseContentDisposition: 'attachment; filename="document.pdf"', responseContentType: "application/pdf", responseCacheControl: "no-cache", }); console.log("Download URL:", signedUrl); })(); ``` ### Delete Files To delete files from an S3 bucket, use the `deleteFile` method: ```typescript (async () => { await s3Pilot.deleteFile("client2", "bucket-B1", { key: "example.jpg", }); console.log("File deleted successfully."); })(); ``` ## File Download Use Cases ### 1. Server-Side Download (Recommended) When you need to control access permissions or avoid CORS issues: ```typescript // In your API endpoint app.get("/download/:fileKey", async (req, res) => { try { const fileResponse = await s3Pilot.getFile("main", "my-bucket", { key: req.params.fileKey, }); res.setHeader("Content-Type", fileResponse.contentType || "application/octet-stream"); res.setHeader("Content-Disposition", `attachment; filename="${req.params.fileKey}"`); res.setHeader("Content-Length", fileResponse.contentLength?.toString() || "0"); res.send(fileResponse.buffer); } catch (error) { res.status(404).json({ error: "File not found" }); } }); ``` ### 2. Enhanced Signed URL for Direct Download When you want the browser to download directly from S3: ```typescript // Generate a signed URL that forces download const downloadUrl = await s3Pilot.generateSignedUrl("main", "my-bucket", { key: fileKey, expiresIn: 3600, // 1 hour responseContentDisposition: `attachment; filename="${fileName}"`, responseContentType: fileType, }); // Redirect user to download URL res.redirect(downloadUrl); ``` ### 3. Streaming Large Files For files larger than 100MB: ```typescript app.get("/stream/:fileKey", async (req, res) => { try { const streamResponse = await s3Pilot.getFileStream("main", "my-bucket", { key: req.params.fileKey, }); res.setHeader("Content-Type", streamResponse.contentType || "application/octet-stream"); res.setHeader("Content-Disposition", `attachment; filename="${req.params.fileKey}"`); res.setHeader("Content-Length", streamResponse.contentLength?.toString() || "0"); streamResponse.stream.pipe(res); } catch (error) { res.status(404).json({ error: "File not found" }); } }); ``` ## Security Considerations - **Private Buckets**: Always use private S3 buckets for sensitive files - **Signed URLs**: Use short expiration times (1 hour max) for signed URLs - **Access Control**: Implement proper authentication before generating download URLs - **CORS**: Configure S3 bucket CORS settings if using direct signed URL downloads - **File Validation**: Validate file types and sizes before allowing downloads ## License This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.