Pulumi(TypeScript、AWS):如何上传含嵌套目录文件的多文件至S3以实现静态网站托管
Solution to Upload Nested Nuxt.js
dist Files to S3 with Pulumi/TypeScript Got it, I’ve faced this exact scenario with Nuxt.js projects and Pulumi before—your original code only handles top-level files in the dist directory, but we can fix that with a recursive file traversal approach. Here's a complete TypeScript solution that uploads every nested file while preserving your directory structure:
Step 1: Full Working Code
First, make sure you have the required dependencies installed (@pulumi/aws, @pulumi/pulumi, mime, and Node.js's built-in fs/path modules—no extra packages needed beyond what you likely already have):
import * as aws from "@pulumi/aws"; import * as pulumi from "@pulumi/pulumi"; import * as mime from "mime"; import * as fs from "fs"; import * as path from "path"; // Create an S3 bucket optimized for Nuxt static/SPA hosting const siteBucket = new aws.s3.Bucket("nuxt-static-site-bucket", { website: { indexDocument: { suffix: "index.html" }, errorDocument: { key: "200.html" }, // Critical for Nuxt SPA routing fallback }, }); // Recursive helper to fetch all files in a directory (including subdirectories) function getAllFiles(directory: string): string[] { const entries = fs.readdirSync(directory, { withFileTypes: true }); // Collect all files in the current directory const files = entries .filter(entry => entry.isFile()) .map(entry => path.join(directory, entry.name)); // Recursively process subdirectories and add their files const subdirectories = entries.filter(entry => entry.isDirectory()); for (const dir of subdirectories) { files.push(...getAllFiles(path.join(directory, dir.name))); } return files; } const distDir = "dist"; // Path to your Nuxt build output const allDistFiles = getAllFiles(distDir); // Upload each file to S3, preserving directory structure allDistFiles.forEach(filePath => { // Use the relative path from dist as the S3 object key (e.g., "_nuxt/fonts/element-icons.ttf") const s3ObjectKey = path.relative(distDir, filePath); new aws.s3.BucketObject(s3ObjectKey, { bucket: siteBucket, source: new pulumi.asset.FileAsset(filePath), contentType: mime.getType(filePath) || undefined, // Optional: Smart cache control for better performance cacheControl: s3ObjectKey.startsWith("_nuxt/") ? "public, max-age=31536000, immutable" // Cache hash-based assets forever : "public, max-age=0, must-revalidate", // Always revalidate HTML/files }); }); // Optional: Add a bucket policy to make your site publicly accessible const publicBucketPolicy = new aws.s3.BucketPolicy("nuxt-site-public-policy", { bucket: siteBucket.bucket, policy: siteBucket.bucket.apply(name => JSON.stringify({ Version: "2012-10-17", Statement: [{ Effect: "Allow", Principal: "*", Action: "s3:GetObject", Resource: `arn:aws:s3:::${name}/*`, }], })), }); // Export useful stack outputs export const bucketName = siteBucket.bucket; export const staticWebsiteUrl = siteBucket.websiteEndpoint;
Key Improvements Explained
- Recursive File Traversal: The
getAllFilesfunction digs into every subdirectory ofdist, so files like_nuxt/fonts/element-icons.4520188.ttfdon’t get missed. - Preserved Directory Structure: Using
path.relative(distDir, filePath)ensures S3 objects have the same nested path as your localdistfiles—critical for Nuxt to load assets correctly. - Nuxt-Specific Bucket Config: The
websitesettings include200.htmlas the error document, which fixes SPA routing issues (Nuxt uses this to handle client-side navigation). - Intelligent Cache Control: Hash-named assets in
_nuxt/are cached indefinitely (since their filenames change on rebuild), while HTML files are revalidated every time—optimizing performance without stale content issues.
How to Use
- Replace your existing Pulumi code with this snippet.
- Run
npm run buildin your Nuxt project to generate thedistdirectory. - Deploy with
pulumi up—all nested files will be uploaded to S3 automatically.
内容的提问来源于stack exchange,提问作者jonashackt
相关产品推荐
相关产品推荐

