2017-04-18 120 views
0

我想通過向我的Node/Express服務器發出請求,將音頻文件從S3傳輸到我的React客戶端。我設法實現了一些可行的方法,但我不確定我是否真的在這裏流式傳輸這個文件,或者直接下載它。我懷疑我可能會下載文件,因爲我對服務器的請求需要很長的時間回來:通過Express服務器從S3流式傳輸音頻需要太長時間

Established database connection. 
Server listening on port 9000! 
::1 - - [18/Apr/2017:21:13:43 +0000] "GET/HTTP/1.1" 200 424 6.933 ms 
::1 - - [18/Apr/2017:21:13:43 +0000] "GET /static/js/main.700ba5c4.js HTTP/1.1" 200 217574 1.730 ms 
::1 - - [18/Apr/2017:21:13:43 +0000] "GET /index.css HTTP/1.1" 200 - 8.722 ms 
Server received a request: GET /tracks 
::1 - - [18/Apr/2017:21:13:43 +0000] "GET /tracks HTTP/1.1" 304 - 41.468 ms 
Server received a request: GET /tracks/1/stream 
::1 - - [18/Apr/2017:21:14:13 +0000] "GET /tracks/1/stream HTTP/1.1" 200 - 636.249 ms 
Server received a request: GET /tracks/2/stream 
Database query threw an error: ETIMEDOUT 

注意636.249毫秒!

你們能否告訴我在這裏做錯了什麼?我粘貼下面我目前的方法的代碼;它具有以下功能:

  1. 客戶端發出一個fetch調用到/tracks/id/stream
  2. 服務器查詢數據庫來獲取軌道
  3. Server使用downloadStream(從s3 package)來獲取文件
  4. 服務器管道數據到客戶端
  5. 客戶端接收數據作爲ArrayBuffer
  6. 客戶端解碼緩衝區並將其傳遞到AudioBufferSourceNode
  7. AudioBufferSourceNode播放音頻

服務器端:

app.get('/tracks/:id/stream', (req, res) => { 

    const id = req.params.id 

    // Query the database for all tracks 
    database.query(`SELECT * FROM Tracks WHERE id = ${id}`, (error, results, fields) => { 

    // Upon failure... 
    if (error) { 
     res.sendStatus(500) 
    } 

    // Upon success... 
    const params = { 
     Bucket: bucketName, // use bucketName defined elsewhere 
     Key: results[0].key // use the key from the track object 
    }; 

    // Download stream and pipe to client 
    const stream = client.downloadStream(params) 
    stream.pipe(res) 
    }) 
}); 

客戶端獲取撥打:

const URL = `/tracks/${id}/stream` 
const options = { method: 'GET' } 

fetch(URL, options) 
.then(response => response.arrayBuffer()) 
.then(data => { 

    // do some stuff 

    AudioPlayer.play(data) 
}) 

客戶端AudioPlayer,負責處理實際AudioBufferSourceNode :

const AudioPlayer = { 
    play: function(data) { 

    // Decode the audio data 
    context.decodeAudioData(data, buffer => { 

     // Create a new buffer source 
     source = context.createBufferSource() 
     source.buffer = buffer 
     source.connect(context.destination) 

     // Start playing immediately 
     source.start(context.currentTime) 
    })  
    }, 
... 
+0

什麼是'client'和'downloadStream()' – peteb

+0

我提到'downloadStream'來自[S3包(https://www.npmjs.com/package/s3);客戶也來自那裏。 – Gundam194

回答

1

這裏有很多錯誤,所以讓我們一個接一個地看看它,不管它是否與你原來的問題有關。

database.query(`SELECT * FROM Tracks WHERE id = ${id}`, (error, results, fields) => { 

在這一行中,您將打開自己的SQL注入攻擊。 從不在沒有正確轉義的情況下,將任意數據連接到查詢的上下文(或任何其他上下文)。無論您使用的是哪種數據庫庫,都會有一個您應該使用的參數化方法。

我懷疑我可能會下載文件,因爲我對服務器的請求需要很長的時間回來

誰知道......你沒有,你向我們展示一個位置我們正在做日誌記錄,所以很難說日誌行是在請求完成之前還是之後。但有一點很清楚,即響應必須至少開始,否則響應狀態碼將不可知。無論如何,來自S3的第一個資源字節的600毫秒響應時間並不是前所未聞的。

  • Server使用downloadStream(從S3包)來獲取文件
  • 服務器管道的數據到客戶端
  • 你在浪費與此有很多帶寬。您不應該獲取文件並將其轉發給客戶端,您應該做的是用15分鐘左右的時間簽署臨時URL,然後將客戶端重定向到該URL。客戶將遵循重定向,現在S3負責處理您的客戶。它會花費你一半的帶寬,更少的CPU資源,並且將從一個更接近你的用戶的位置交付。您可以使用AWS JS SDK創建此簽名的URL。

  • 客戶機接收到的數據作爲ArrayBuffer
  • 有沒有流發生在這裏。您的客戶在播放任何內容之前正在下載整個資源。

    應該做的是創建一個正常的音頻實例。它會自動從您的Node.js應用程序重定向到您已簽名的S3 URL,併爲您處理所有緩衝和流式傳輸。

    let a = new Audio('/tracks/' + encodeURIComponent(id) + '/stream'); 
    a.play(); 
    
    相關問題