我来自Django背景,静态文件主要存储在S3上,我想了解它在NodeJS上的工作原理,因为我想将应用程序从Django / React迁移到NodeJS / ExpressJS / React。
我不知道如何在生产环境中将静态文件(客户端js,css,图像)存储在哪里?我想知道how to upload to s3和管理动态文件,因为工作是由用户通过express api完成的,但是我正在寻找可以将所有公共文件批量上传到其中的内容s3部署(这是否与express正确?)。
由于我想部署到Heroku,所以我知道它们有一个不保留这些静态文件的策略,(在Django中,每次部署时我都使用“ collectstatic”命令将所有静态文件批量上传到S3),如何&您在哪里从哪里提供这些文件?
任何建议都会有所帮助。
答案 0 :(得分:0)
您对Heroku的来源有“不保留那些静态文件的政策”吗?
的确,如果您想将图像上传功能添加到您的应用中,那么S3之类的解决方案可能会有所帮助,因为Heroku使用的dynos(隔离Linux进程)不允许动态写入文件系统。
除了这种用例(“用户应该能够上传文件”)之外,对于静态文件使用S3似乎是不必要的复杂性。
用于提供静态文件的NodeJS API是:
app.use(express.static(path.join(__dirname, 'build')));
要在Heroku上试用此API,我将使用NodeJS的静态文件API部署此barebones sample NodeJS/Express/React App。
回购使用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 and Express一起运行,我将结帐: This StackOverflow Thread.
答案 1 :(得分:0)
next.config.js ,将assetPrefix设置为生产中的aws s3链接。
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。