Node.js 在express/nodejs应用程序中为存储在S3中的文件提供服务

Node.js 在express/nodejs应用程序中为存储在S3中的文件提供服务,node.js,nginx,amazon-s3,express,fileserver,Node.js,Nginx,Amazon S3,Express,Fileserver,我有一个应用程序,其中用户的照片是私人的。我将照片(缩略图)存储在AWS s3中。网站中有一个页面,用户可以在其中查看他的照片(即缩略图)。现在我的问题是如何提供这些文件。我评估过的一些选项包括: 使用签名url生成从CloudFront(或AWS)服务文件。但问题是每次用户刷新页面时,我都必须再次创建这么多已签名的URL并加载它。因此,我将无法在浏览器中缓存图像,这将是一个不错的选择。在javascript中还有什么需要做的吗?由于安全问题,我不能拥有这些URL的有效期更长。第二,在这个时间

我有一个应用程序,其中用户的照片是私人的。我将照片(缩略图)存储在AWS s3中。网站中有一个页面,用户可以在其中查看他的照片(即缩略图)。现在我的问题是如何提供这些文件。我评估过的一些选项包括:

  • 使用签名url生成从CloudFront(或AWS)服务文件。但问题是每次用户刷新页面时,我都必须再次创建这么多已签名的URL并加载它。因此,我将无法在浏览器中缓存图像,这将是一个不错的选择。在javascript中还有什么需要做的吗?由于安全问题,我不能拥有这些URL的有效期更长。第二,在这个时间范围内,如果有人获得了这个url,他就可以查看文件,而无需通过应用程序的身份验证
  • 另一个选择是在从S3服务器流式传输文件后,从my express应用程序本身提供文件。这允许我拥有http缓存头,因此启用浏览器缓存。它还确保没有经过身份验证的任何人都无法查看文件。理想情况下,我希望使用NGINX代理将文件和我托管的主机流传输到NGINX的另一端。但正如我所见,只有当文件存在于同一系统的文件中时,这才可能实现。但是在这里,我必须对它进行流式处理,并在流完成后返回。不希望将文件存储在本地
我无法评估这两个选项中哪一个是更好的选择??我希望将尽可能多的工作重定向到S3或cloudfront,但即使使用单一URL,也会首先向我的服务器发出请求。我还需要缓存功能


那么,理想的做法是什么呢?关于这些方法的特定问题的答案?

如果照片确实需要保密,我会考虑使用CloudFront选项。看起来您在管理自己的安全策略时会有更多的灵活性。我认为nginx的设置可能比需要的更复杂。Express作为一个远程代理,应该可以提供非常好的性能,它使用请求从S3获取项目,并将它们流式传输给授权用户。我强烈建议大家看看Asset Rack,它使用散列签名在浏览器中启用永久缓存。您将无法使用默认机架,因为您需要计算每个文件的MD5(可能是在上载时?),而流式传输时您无法这样做。但是,根据应用程序的不同,浏览器无需重新蚀刻图像,这可以为您节省大量工作。

关于第二个选项,您应该能够设置

关于你的第一个选择。您是否考虑过以不同的方式保护您的图像? 在S3中存储图像时,不能使用散列和随机文件名吗?这将是相当直接的文件名难以猜测+这样你就不会有性能问题查看图像回来


这是facebook使用的技术。只要知道URL,您在注销时仍然可以查看图像。

我只想从S3流式传输它。这很容易,而签名URL则要困难得多。将图像上载到S3时,只需确保设置了
内容类型
内容长度
标题

var aws = require('knox').createClient({
  key: '',
  secret: '',
  bucket: ''
})

app.get('/image/:id', function (req, res, next) {
  if (!req.user.is.authenticated) {
    var err = new Error()
    err.status = 403
    next(err)
    return
  }

  aws.get('/image/' + req.params.id)
  .on('error', next)
  .on('response', function (resp) {
    if (resp.statusCode !== 200) {
      var err = new Error()
      err.status = 404
      next(err)
      return
    }

    res.setHeader('Content-Length', resp.headers['content-length'])
    res.setHeader('Content-Type', resp.headers['content-type'])

    // cache-control?
    // etag?
    // last-modified?
    // expires?

    if (req.fresh) {
      res.statusCode = 304
      res.end()
      return
    }

    if (req.method === 'HEAD') {
      res.statusCode = 200
      res.end()
      return
    }

    resp.pipe(res)
  })
})

如果您使用
302将用户重定向到已签名的url,则Found
浏览器将根据其
缓存控件
标题缓存生成的图像,并且不会第二次请求它

要防止浏览器缓存已签名的url本身,您应将适当的
缓存控制
标题与之一起发送:

Cache-Control: private, no-cache, no-store, must-revalidate
因此,下次它将向原始url发送请求,并将重定向到新的签名url

您可以使用
knox
生成签名url

但不要忘记为每个上传的图像设置适当的标题。我建议您同时使用
缓存控制
过期
标题,因为某些浏览器不支持
缓存控制
标题,而
过期
只允许您设置绝对过期时间

使用第二个选项(通过应用程序传输图像),您可以更好地控制情况。例如,您将能够根据当前日期和时间为每个响应生成
Expires
标题

但是速度呢?使用签名URL有两个优点,这可能会影响页面加载速度

首先,您不会使服务器过载。如果生成签名URL很快,因为您只是在散列您的AWS凭据。要在服务器上传输图像,您需要在页面加载期间保持大量额外的连接。无论如何,除非您的服务器是硬加载的,否则不会有任何实际的区别

其次,浏览器在页面加载期间每个主机名只保持两个并行连接。所以,在下载图像和URL时,浏览器将保持并行解析图像和URL。它还可以防止图像下载阻止任何其他资源的下载

无论如何,为了绝对确定您应该运行一些基准测试。我的回答是基于我对HTTP规范的了解和我在web开发方面的经验,但我自己从未尝试过以这种方式提供图像。直接从S3提供具有长缓存生存期的公共映像可以提高页面速度,我相信如果您通过重定向来实现这一点,情况不会改变

您应该记住,通过服务器传输图像将使Amazon CloudFront的所有好处化为乌有。但只要您直接从S3提供内容,这两个选项都可以正常工作

因此,有两种情况下,使用签名URL会加快页面速度:

  • 如果你在一个页面上有很多图片
  • 如果您使用CloudFront提供图像
如果您在每个页面上只有很少的图像,并且直接从S3提供它们,那么您可能会