簡體   English   中英

在express / nodejs app中提供存儲在S3中的文件

[英]Serving files stored in S3 in express/nodejs app

我有應用程序用戶的照片是私人的。 我將照片(縮略圖也)存儲在AWS s3中。 網站中有一個頁面,用戶可以在其中查看他的照片(即縮略圖)。 現在我的問題是如何提供這些文件。 我評估的一些選項是:

  • 使用簽名的URL生成從CloudFront(或AWS)提供文件。 但問題是,每次用戶刷新頁面時,我必須再次創建這么多已簽名的URL並加載它。 因此,我無法在瀏覽器中緩存圖像本來是一個不錯的選擇。 無論如何仍然在javascript中做? 由於安全問題,我不能長時間保持這些網址的有效性。 其次,在該時間范圍內,如果有人抓住該網址,他可以查看該文件,而無需通過應用程序進行身份驗證。
  • 其他選項是在從S3服務器流式傳輸后從我的快遞應用程序本身提供文件。 這允許我有http緩存頭,因此啟用瀏覽器緩存。 它還確保沒有人可以在未經過身份驗證的情況下查看文件。 理想情況下,我想流式傳輸文件和我使用NGINX代理中繼托管另一端流式傳輸到NGINX。 但正如我所見,只有文件存在於同一系統的文件中才能實現。 但是在這里我必須流式傳輸並在我完成流時返回。 不想在本地存儲文件。

我無法評估這兩個選項中哪一個是更好的選擇? 我想盡可能多地將工作重定向到S3或cloudfront,但即使使用了singed url也會首先向我的服務器發出請求。 我也想要緩存功能。

那么理想的做法是什么? 有關這些方法的特定問題的答案?

我會從S3流式傳輸它。 它非常簡單,簽名的URL要困難得多。 只需確保在將圖像上傳到S3時設置content-typecontent-length標題。

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 Found將用戶重定向到已簽名的URL,則會根據其cache-control標頭緩存生成的圖像,並且不會再次詢問它。

為防止瀏覽器緩存已簽名的URL本身,您應該發送適當的Cache-Control標頭:

Cache-Control: private, no-cache, no-store, must-revalidate

因此,下次它會向原始網址發送請求,並將重定向到新簽名的網址。

您可以使用signedUrl方法使用knox生成簽名的URL。

但是不要忘記為每個上傳的圖像設置正確的標題。 我建議您同時使用Cache-ControlExpires標頭,因為某些瀏覽器不支持Cache-Control標頭, Expires允許您僅設置絕對過期時間。

使用第二個選項(通過您的應用程序流式傳輸圖像),您可以更好地控制情況。 例如,您將能夠根據當前日期和時間為每個響應生成Expires標頭。

但速度怎么樣? 使用簽名的URL有兩個優點,可能會影響頁面加載速度。

首先,您不會使服務器過載。 如果快速生成簽名的URL,因為您只是哈希您的AWS憑據。 要通過服務器流式傳輸圖像,您需要在頁面加載期間保持大量額外連接。 無論如何,除非您的服務器是硬加載的,否則它不會產生任何實際差異。

其次,瀏覽器在頁面加載期間每個主機名只保留兩個並行連接。 因此,瀏覽器將在下載時保持並行解析圖像網址。 它還可以阻止從任何其他資源的下載下載圖像。

無論如何,要絕對確定你應該運行一些基准測試。 我的回答是基於我對HTTP規范的了解以及我在Web開發方面的經驗,但我從未試圖以自己的方式提供圖像。 直接從S3提供具有長緩存生命周期的公共圖像可以提高頁面速度,我相信如果你通過重定向來實現它,情況不會改變。

您應該記住,通過您的服務器流式傳輸圖像將帶來Amazon CloudFront的所有好處。 但只要您直接從S3提供內容,兩個選項都可以正常工作。

因此,在使用簽名網址時,有兩種情況應該加速您的網頁:

  • 如果您在一個頁面上有很多圖像。
  • 如果您使用CloudFront提供圖像。

如果您在每個頁面上只有很少的圖像並直接從S3提供它們,您可能根本不會看到任何差異。

重要更新

我運行了一些測試,發現我對緩存有誤。 確實,瀏覽器會緩存重定向到的圖像。 但它將緩存的圖像與重定向到的URL相關聯,而不是與原始圖像相關聯。 因此,當瀏覽器第二次加載頁面時,它再次從服務器請求圖像,而不是從緩存中獲取圖像。 當然,如果服務器使用相同的重定向URL響應它第一次響應,瀏覽器將使用其緩存,但簽名網址不是這種情況。

我發現強制瀏覽器緩存已簽名的URL以及它收到的數據可以解決問題。 但我不喜歡緩存無效重定向URL的想法。 我的意思是,如果瀏覽器以某種方式錯過圖像,它將嘗試使用緩存中的無效簽名URL再次請求它。 所以,我認為這不是一個選擇。

如果CloudFront更快地提供圖像或瀏覽器限制每個主機名的並行下載數量並不重要,使用瀏覽器緩存的優勢超出了通過服務器管道圖像的所有缺點。

看起來大多數社交網絡通過將其實際網址隱藏在某些私有代理后面來解決私有圖像的問題。 因此,他們將所有內容存儲在公共服務器上,但未經授權就無法獲取私有映像的URL。 當然,如果您在新標簽頁中打開私人圖片並將網址發送給您的朋友,他也可以看到圖片。 所以,如果它不適合你,那么你最好使用Jonathan Ong的解決方案

如果照片真的需要保密,我會擔心使用CloudFront選項。 您似乎可以更靈活地管理自己的安全策略。 我認為nginx設置可能比必要的更復雜。 Express應該作為遠程代理提供非常好的性能,它使用請求從S3獲取項目並將它們傳遞給授權用戶。 我強烈建議您查看Asset Rack,它使用哈希簽名在瀏覽器中啟用永久緩存。 您將無法使用默認機架,因為您需要計算每個文件的MD5(可能在上傳?),這是您在流式傳輸時無法做到的。 但是根據您的應用程序,它可以為您節省大量的工作量,因為瀏覽器永遠不需要重新獲取圖像。

關於第二個選項,您應該能夠直接在S3中設置緩存控制頭

關於你的第一個選擇。 您是否考慮過以不同方式保護圖像? 在S3中存儲圖像時,是否不能使用散列和隨機文件名? 使文件名難以猜測是非常直接的+這樣你就沒有查看圖像的性能問題了。

這是facebook使用的技術。 只要您知道URL,您仍然可以在注銷時查看圖像。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM