@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
Markdown
# 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.