【问题标题】:How do I host static files on AWS S3 using next?如何使用 next 在 AWS S3 上托管静态文件?
【发布时间】:2019-11-30 16:59:42
【问题描述】:

我来自 Django 背景,其中静态文件主要存储在 S3 上,我试图了解它在 NodeJS 上的工作原理,因为我想将应用程序从 Django/React 迁移到 NodeJS/NextJS/ExpressJS/反应。

我不知道在生产环境中如何以及在何处存储我的静态文件(客户端 js、css、图像)?我想知道how to upload to s3 并管理 dynamic 文件,因为工作是由用户通过 express api 完成的,但我正在寻找可以将所有公共文件批量上传到 s3 的东西deploy(这甚至是使用 express 的正确方法吗?)。

由于我想部署到 Heroku,我知道他们有不保留这些静态文件的政策,(在 Django 中,我使用“collectstatic”命令在每次部署时将我的所有静态文件批量上传到 S3),如何以及您从哪里提供这些文件?

任何建议都会有所帮助。

【问题讨论】:

  • 我不清楚问题是什么。您说您知道如何将内容上传到 S3,但您问的是如何将内容上传到 S3?或者,您是否在寻找类似 collectstatic 的东西?
  • @Brad 动态文件和静态文件之间存在差异,我正在寻找类似“collectstatic”的东西,其中文件夹中的每个文件都在部署时上传到 s3。我想知道这样做是否正确?
  • 通常,我只是编写一个使用s3cmd 进行上传的小部署脚本。

标签: node.js heroku amazon-s3 next.js


【解决方案1】:

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。

【讨论】:

  • ISS 怎么样?新的静态生成的运行时数据将存储在哪里,它们不会存储在 s3 存储桶中
【解决方案2】:

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

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 StackOverflow Thread.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-05-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-16
    • 2012-07-27
    • 2017-03-15
    相关资源
    最近更新 更多