UNPKG

@ahhaohho/s3-upload-sdk

Version:

S3 + CloudFront presigned URL SDK for AhhaOhho platform

366 lines (283 loc) 8.28 kB
# @ahhaohho/s3-upload-sdk S3 + CloudFront presigned URL SDK for AhhaOhho platform. This SDK allows clients to upload files directly to S3 using presigned URLs and get CloudFront URLs for the uploaded files. ## Features - 🚀 Direct upload to S3 using presigned URLs - ☁️ CloudFront URL support for fast content delivery - 📦 TypeScript support with full type definitions - 📊 Upload progress tracking - 🔄 Multiple file upload support - ✅ File validation (size, type) - 🔐 Authorization token support ## Installation ```bash npm install @ahhaohho/s3-upload-sdk ``` ## Usage ### Basic Usage ```typescript import { S3UploadClient } from '@ahhaohho/s3-upload-sdk'; // Initialize the client const uploadClient = new S3UploadClient({ apiBaseUrl: 'https://api.ahhaohho.com', authToken: 'your-auth-token', // Optional }); // Upload a file const fileInput = document.getElementById('fileInput') as HTMLInputElement; const file = fileInput.files[0]; try { const result = await uploadClient.upload({ file: file, folder: 'announcements', // Folder in S3 onProgress: (progress) => { console.log(`Upload progress: ${progress}%`); }, }); console.log('File uploaded successfully!'); console.log('CloudFront URL:', result.url); console.log('S3 Key:', result.key); console.log('File size:', result.size, 'bytes'); } catch (error) { console.error('Upload failed:', error); } ``` ### Upload with Custom Filename ```typescript const result = await uploadClient.upload({ file: file, folder: 'profiles', filename: 'user-avatar', // Will be: user-avatar-1234567890-abc123.jpg contentType: 'image/jpeg', }); ``` ### Upload Multiple Files ```typescript const files = Array.from(fileInput.files); const results = await uploadClient.uploadMultiple( files.map((file) => ({ file, folder: 'announcements', })) ); const urls = results.map((r) => r.url); console.log('All files uploaded:', urls); ``` ### File Validation ```typescript const validation = uploadClient.validateFile( file, 5, // Max 5MB ['image/jpeg', 'image/png', 'image/gif'] // Allowed types ); if (!validation.valid) { alert(validation.error); return; } // Proceed with upload const result = await uploadClient.upload({ file, folder: 'announcements', }); ``` ### Update Auth Token ```typescript // Update token after login uploadClient.setAuthToken(newToken); ``` ### React Example ```tsx import React, { useState } from 'react'; import { S3UploadClient } from '@ahhaohho/s3-upload-sdk'; const uploadClient = new S3UploadClient({ apiBaseUrl: process.env.REACT_APP_API_URL, }); function ImageUploader() { const [uploading, setUploading] = useState(false); const [progress, setProgress] = useState(0); const [imageUrl, setImageUrl] = useState(''); const handleUpload = async (e: React.ChangeEvent<HTMLInputElement>) => { const file = e.target.files?.[0]; if (!file) return; // Validate file const validation = uploadClient.validateFile(file, 10, [ 'image/jpeg', 'image/jpg', 'image/png', 'image/gif', ]); if (!validation.valid) { alert(validation.error); return; } try { setUploading(true); setProgress(0); const result = await uploadClient.upload({ file, folder: 'announcements', onProgress: (p) => setProgress(p), }); setImageUrl(result.url); alert('Upload successful!'); } catch (error) { console.error('Upload failed:', error); alert('Upload failed'); } finally { setUploading(false); } }; return ( <div> <input type="file" accept="image/*" onChange={handleUpload} disabled={uploading} /> {uploading && <div>Upload progress: {progress}%</div>} {imageUrl && <img src={imageUrl} alt="Uploaded" />} </div> ); } export default ImageUploader; ``` ### Vue Example ```vue <template> <div> <input type="file" accept="image/*" @change="handleUpload" :disabled="uploading" /> <div v-if="uploading">Upload progress: {{ progress }}%</div> <img v-if="imageUrl" :src="imageUrl" alt="Uploaded" /> </div> </template> <script setup lang="ts"> import { ref } from 'vue'; import { S3UploadClient } from '@ahhaohho/s3-upload-sdk'; const uploadClient = new S3UploadClient({ apiBaseUrl: import.meta.env.VITE_API_URL, }); const uploading = ref(false); const progress = ref(0); const imageUrl = ref(''); const handleUpload = async (e: Event) => { const target = e.target as HTMLInputElement; const file = target.files?.[0]; if (!file) return; // Validate file const validation = uploadClient.validateFile(file, 10, [ 'image/jpeg', 'image/jpg', 'image/png', 'image/gif', ]); if (!validation.valid) { alert(validation.error); return; } try { uploading.value = true; progress.value = 0; const result = await uploadClient.upload({ file, folder: 'announcements', onProgress: (p) => (progress.value = p), }); imageUrl.value = result.url; alert('Upload successful!'); } catch (error) { console.error('Upload failed:', error); alert('Upload failed'); } finally { uploading.value = false; } }; </script> ``` ## API Reference ### `S3UploadClient` #### Constructor ```typescript new S3UploadClient(config: S3UploadConfig) ``` **Parameters:** - `config.apiBaseUrl` (string): Base URL of your API server - `config.authToken` (string, optional): Authorization token - `config.headers` (object, optional): Custom headers #### Methods ##### `upload(options: UploadOptions): Promise<UploadResult>` Upload a single file. **Parameters:** - `options.file` (File | Blob): File to upload - `options.folder` (string): Folder path in S3 - `options.filename` (string, optional): Custom filename - `options.contentType` (string, optional): Content type (MIME type) - `options.onProgress` (function, optional): Progress callback **Returns:** - `url` (string): CloudFront URL - `key` (string): S3 key - `size` (number): File size in bytes ##### `uploadMultiple(files: UploadOptions[]): Promise<UploadResult[]>` Upload multiple files in parallel. ##### `validateFile(file, maxSizeMB, allowedTypes): { valid: boolean, error?: string }` Validate file before upload. ##### `setAuthToken(token: string): void` Update authorization token. ## Server-side Setup (Backend Only) **Important**: AWS credentials are ONLY needed on your API server, NOT in the client SDK. The SDK communicates with your API server, which then generates presigned URLs using AWS credentials. You need to implement a presigned URL endpoint on your backend server: ```typescript // Example Express endpoint app.post('/communities/s3/presigned-url', async (req, res) => { const { folder, filename, contentType } = req.body; // Generate presigned URL using AWS SDK const s3 = new S3Client({ region: 'ap-northeast-2' }); const key = `raw/${folder}/${filename}`; const command = new PutObjectCommand({ Bucket: 'your-bucket-name', Key: key, ContentType: contentType, }); const uploadUrl = await getSignedUrl(s3, command, { expiresIn: 300 }); const publicUrl = `https://your-cloudfront-domain.cloudfront.net/${key}`; res.json({ type: 'success', status: 200, data: { uploadUrl, publicUrl, key, expiresIn: 300, }, message: null, }); }); ``` See the [server implementation guide](./SERVER_SETUP.md) for detailed instructions. ## Error Handling ```typescript try { const result = await uploadClient.upload({ file, folder: 'announcements' }); } catch (error) { if (error.code === 'PRESIGNED_URL_ERROR') { console.error('Failed to get presigned URL:', error.message); } else if (error.code === 'S3_UPLOAD_ERROR') { console.error('Failed to upload to S3:', error.message); } else { console.error('Unknown error:', error); } } ``` ## TypeScript Support This package is written in TypeScript and includes type definitions. ```typescript import type { UploadResult, UploadOptions } from '@ahhaohho/s3-upload-sdk'; ``` ## License ISC ## Contributing Issues and pull requests are welcome!