如何使用 next 在 AWS S3 上托管静态文件?
How do I host static files on AWS S3 using next?
我来自 Django 背景,其中静态文件主要存储在 S3 上,我试图了解它在 NodeJS 上的工作方式,因为我想将应用程序从 Django/React 迁移到 NodeJS/NextJS/ExpressJS/React.
我不知道在生产环境中如何以及在何处存储我的静态文件(客户端 js、css、图像)?我想了解 how to upload to s3 并管理 动态 文件,因为这项工作是由用户通过快递 api 完成的,但我正在寻找我可以找到的东西在部署时将所有 public 文件批量上传到 s3(这甚至是使用 express 的正确方法吗?)。
因为我想部署到 Heroku,我知道他们有一个不保留这些静态文件的策略,(在 Django 中,我使用 "collectstatic" 命令将我所有的静态文件批量上传到 S3部署),如何以及从哪里提供这些文件?
任何建议都会有所帮助。
你的 Heroku 来源是什么"a policy of not keeping those static files"?
确实,如果您想将图像上传功能添加到您的应用程序,那么像 S3 这样的解决方案可能会有所帮助,因为 Heroku 使用不允许的 dynos(隔离 Linux 进程)用于动态写入文件系统。
除了那个用例 ("users should be able to upload files"),对静态文件使用 S3 似乎是不必要的复杂性。
用于提供静态文件的 NodeJS API 是:
app.use(express.static(path.join(__dirname, 'build')));
为了在 Heroku 上试验这个 API,我会使用 NodeJS 的静态文件 API.
部署这个 barebones sample NodeJS/Express/React App
repo 使用 npm 的 react-scripts
库来打包和编译 React 代码,服务器只是提供打包到动态生成的 '/build'
文件夹中的文件。
因此您的服务器代码变得如此简单:
const express = require('express');
const http = require('http');
const path = require('path');
let app = express();
app.use(express.static(path.join(__dirname, 'build')));
const port = process.env.PORT || '8080';
app.set('port', port);
const server = http.createServer(app);
server.listen(port);
如果你真的想要 S3 运行 Node 和 Express 我会签出:
This Whosebug Thread.
next.config.js,在生产中将 assetPrefix 设置为您的 aws s3 link。
const isDev = process.env.NODE_ENV !== 'production';
const version = require('./package.json').version;
assetPrefix: isDev ? '' : `https://${process.env.AWS_REGION}.amazonaws.com/${process.env.AWS_S3_BUCKET_NAME}/${version}`,
collectstatic.js,运行 在构建后版本上。
require('dotenv').config();
const fs = require('fs');
const readDir = require('recursive-readdir');
const path = require('path');
const AWS = require('aws-sdk');
const mime = require('mime-types');
const version = require('./package.json').version;
// You will run this script from your CI/Pipeline after build has completed.
// It will read the content of the build directory and upload to S3 (live assets bucket)
// Every deployment is immutable. Cache will be invalidated every time you deploy.
AWS.config.update({
region: process.env.AWS_S3_REGION,
accessKeyId: process.env.AWS_S3_ACCESS_KEY,
secretAccessKey: process.env.AWS_S3_SECRET_KEY,
maxRetries: 3
});
// Retrive al the files path in the build directory
const getDirectoryFilesRecursive = (dir, ignores = []) => {
return new Promise((resolve, reject) => {
readDir(dir, ignores, (err, files) => (err ? reject(err) : resolve(files)));
});
};
// The Key will look like this: _next/public/<buildid>/pages/index.js
// the <buildid> is exposed by nextJS and it's unique per deployment.
// See: https://nextjs.org/blog/next-7/#static-cdn-support
const generateFileKey = (fileName, toReplace, replaced) => {
// I'm interested in only the last part of the file: '/some/path/.next/build-manifest.json',
const S3objectPath = fileName.split(toReplace)[1];
return version + replaced + S3objectPath;
};
const s3 = new AWS.S3();
const uploadToS3 = async (fileArray, toReplace, replaced) => {
try {
fileArray.map(file => {
// Configuring parameters for S3 Object
const S3params = {
Bucket: process.env.AWS_S3_BUCKET_NAME,
Body: fs.createReadStream(file),
Key: generateFileKey(file, toReplace, replaced),
ACL: 'public-read',
ContentType: String(mime.lookup(file)),
ContentEncoding: 'utf-8',
CacheControl: 'immutable,max-age=31536000,public'
};
s3.upload(S3params, function(err, data) {
if (err) {
// Set the exit code while letting
// the process exit gracefully.
console.error(err);
process.exitCode = 1;
} else {
console.log(`Assets uploaded to S3:`, data.key);
}
});
});
} catch (error) {
console.error(error);
}
};
// Start function
// getDirectoryFilesRecursive(path, ignore);
const start = async function(dict) {
for (var i = 0; i < dict.length; i++) {
const files = await getDirectoryFilesRecursive(path.resolve(__dirname, dict[i].filePath), ['.DS_Store', 'BUILD_ID']);
uploadToS3(files, dict[i].toReplace, dict[i].replaced);
}
}
// Call start
start([
{
filePath: '.next',
toReplace: '.next/',
replaced: '/_next/'
}
]);
运行 node collectstatic.js
将您的所有资产上传到 S3。
我来自 Django 背景,其中静态文件主要存储在 S3 上,我试图了解它在 NodeJS 上的工作方式,因为我想将应用程序从 Django/React 迁移到 NodeJS/NextJS/ExpressJS/React.
我不知道在生产环境中如何以及在何处存储我的静态文件(客户端 js、css、图像)?我想了解 how to upload to s3 并管理 动态 文件,因为这项工作是由用户通过快递 api 完成的,但我正在寻找我可以找到的东西在部署时将所有 public 文件批量上传到 s3(这甚至是使用 express 的正确方法吗?)。
因为我想部署到 Heroku,我知道他们有一个不保留这些静态文件的策略,(在 Django 中,我使用 "collectstatic" 命令将我所有的静态文件批量上传到 S3部署),如何以及从哪里提供这些文件?
任何建议都会有所帮助。
你的 Heroku 来源是什么"a policy of not keeping those static files"?
确实,如果您想将图像上传功能添加到您的应用程序,那么像 S3 这样的解决方案可能会有所帮助,因为 Heroku 使用不允许的 dynos(隔离 Linux 进程)用于动态写入文件系统。
除了那个用例 ("users should be able to upload files"),对静态文件使用 S3 似乎是不必要的复杂性。
用于提供静态文件的 NodeJS API 是:
app.use(express.static(path.join(__dirname, 'build')));
为了在 Heroku 上试验这个 API,我会使用 NodeJS 的静态文件 API.
部署这个 barebones sample NodeJS/Express/React Apprepo 使用 npm 的 react-scripts
库来打包和编译 React 代码,服务器只是提供打包到动态生成的 '/build'
文件夹中的文件。
因此您的服务器代码变得如此简单:
const express = require('express');
const http = require('http');
const path = require('path');
let app = express();
app.use(express.static(path.join(__dirname, 'build')));
const port = process.env.PORT || '8080';
app.set('port', port);
const server = http.createServer(app);
server.listen(port);
如果你真的想要 S3 运行 Node 和 Express 我会签出: This Whosebug Thread.
next.config.js,在生产中将 assetPrefix 设置为您的 aws s3 link。
const isDev = process.env.NODE_ENV !== 'production';
const version = require('./package.json').version;
assetPrefix: isDev ? '' : `https://${process.env.AWS_REGION}.amazonaws.com/${process.env.AWS_S3_BUCKET_NAME}/${version}`,
collectstatic.js,运行 在构建后版本上。
require('dotenv').config();
const fs = require('fs');
const readDir = require('recursive-readdir');
const path = require('path');
const AWS = require('aws-sdk');
const mime = require('mime-types');
const version = require('./package.json').version;
// You will run this script from your CI/Pipeline after build has completed.
// It will read the content of the build directory and upload to S3 (live assets bucket)
// Every deployment is immutable. Cache will be invalidated every time you deploy.
AWS.config.update({
region: process.env.AWS_S3_REGION,
accessKeyId: process.env.AWS_S3_ACCESS_KEY,
secretAccessKey: process.env.AWS_S3_SECRET_KEY,
maxRetries: 3
});
// Retrive al the files path in the build directory
const getDirectoryFilesRecursive = (dir, ignores = []) => {
return new Promise((resolve, reject) => {
readDir(dir, ignores, (err, files) => (err ? reject(err) : resolve(files)));
});
};
// The Key will look like this: _next/public/<buildid>/pages/index.js
// the <buildid> is exposed by nextJS and it's unique per deployment.
// See: https://nextjs.org/blog/next-7/#static-cdn-support
const generateFileKey = (fileName, toReplace, replaced) => {
// I'm interested in only the last part of the file: '/some/path/.next/build-manifest.json',
const S3objectPath = fileName.split(toReplace)[1];
return version + replaced + S3objectPath;
};
const s3 = new AWS.S3();
const uploadToS3 = async (fileArray, toReplace, replaced) => {
try {
fileArray.map(file => {
// Configuring parameters for S3 Object
const S3params = {
Bucket: process.env.AWS_S3_BUCKET_NAME,
Body: fs.createReadStream(file),
Key: generateFileKey(file, toReplace, replaced),
ACL: 'public-read',
ContentType: String(mime.lookup(file)),
ContentEncoding: 'utf-8',
CacheControl: 'immutable,max-age=31536000,public'
};
s3.upload(S3params, function(err, data) {
if (err) {
// Set the exit code while letting
// the process exit gracefully.
console.error(err);
process.exitCode = 1;
} else {
console.log(`Assets uploaded to S3:`, data.key);
}
});
});
} catch (error) {
console.error(error);
}
};
// Start function
// getDirectoryFilesRecursive(path, ignore);
const start = async function(dict) {
for (var i = 0; i < dict.length; i++) {
const files = await getDirectoryFilesRecursive(path.resolve(__dirname, dict[i].filePath), ['.DS_Store', 'BUILD_ID']);
uploadToS3(files, dict[i].toReplace, dict[i].replaced);
}
}
// Call start
start([
{
filePath: '.next',
toReplace: '.next/',
replaced: '/_next/'
}
]);
运行 node collectstatic.js
将您的所有资产上传到 S3。