2015-11-18 79 views
6

我正在使用Node.js創建媒體上傳微服務。該服務通過將上傳的二進制數據接收到緩衝區,然後使用S3 npm軟件包上載到S3存儲桶來工作。我試圖使用該包中的eventEmitter來顯示上傳到S3的數據量,並將其發送回正在上傳的客戶端(以便他們可以看到上傳進度)。我使用socket.io發送進度數據給客戶端。Socket.io無法將數據發送到客戶端的獨特空間

我遇到的問題是,socket.io中的.emit事件會將上載進度數據發送給所有連接的客戶端,而不僅僅是發起上傳的客戶端。據我所知,一個套接字連接到'連接'上的默認房間,它由客戶端的'id'鏡像。根據官方文檔,使用socket.to(id).emit()應該將數據範圍僅發送給該客戶端,但這不適用於我。

更新了例代碼:

server.js:

var http = require('http'), 
users = require('./data'), 
app = require('./app')(users); 

var server = http.createServer(app); 

server.listen(app.get('port'), function(){ 
    console.log('Express server listening on port ' + app.get('port')); 
}); 

var io = require('./socket.js').listen(server); 

socket.js:

var socketio = require('socket.io'); 

var socketConnection = exports = module.exports = {}; 

socketConnection.listen = function listen(app) { 
    io = socketio.listen(app); 
    exports.sockets = io.sockets; 

    io.sockets.on('connection', function (socket) { 
     socket.join(socket.id); 
     socket.on('disconnect', function(){ 
      console.log("device "+socket.id+" disconnected"); 
     }); 
     socketConnection.upload = function upload (data) { 
     socket.to(socket.id).emit('progress', {progress:(data.progressAmount/data.progressTotal)*100}); 
    }; 
}); 
return io; 
}; 

s3upload.js:

var config = require('../config/aws.json'); 
var s3 = require('s3'); 
var path = require('path'); 
var fs = require('fs'); 
var Busboy = require('busboy'); 
var inspect = require('util').inspect; 

var io = require('../socket.js'); 
... 
var S3Upload = exports = module.exports = {}; 
.... 
S3Upload.upload = function upload(params) { 
// start uploading to uploader 
var uploader = client.uploadFile(params); 

uploader.on('error', function(err) { 
    console.error("There was a problem uploading the file to bucket, either the params are incorrect or there is an issue with the connection: ", err.stack); 
    res.json({responseHTML: "<span>There was a problem uploading the file to bucket, either the params are incorrect or there is an issue with the connection. Please refresh and try again.</span>"}); 
    throw new Error(err); 
}), 

uploader.on('progress', function() { 
    io.upload(uploader); 
}), 

uploader.on('end', function(){ 
    S3Upload.deleteFile(params.localFile); 
}); 
}; 

當使用DEBUG = *節點myapp.js,我看到了socket.io-pa RSER取入該信息,但它不是它發射到客戶端:

socket.io-parser encoding packet {"type":2,"data":["progress",{"progress":95.79422221709825}],"nsp":"/"} +0ms 


socket.io-parser encoded {"type":2,"data":["progress",{"progress":95.79422221709825}],"nsp":"/"} as 2["progress",{"progress":95.79422221709825}] +0ms 

然而,如果刪除該代碼的。要部分,它發送數據到客戶端(儘管到所有客戶,這將不利於在所有):

io.sockets.on('connection', function(socket) { 
    socket.join(socket.id); 
    socket.emit('progress', {progress: (data.progressAmount/data.progressTotal)*100}); 
}); 

DEBUG = *節點myapp.js:

socket.io:client writing packet {"type":2,"data":["progress",{"progress":99.93823786632886}],"nsp":"/"} +1ms 
    socket.io-parser encoding packet {"type":2,"data":["progress",{"progress":99.93823786632886}],"nsp":"/"} +1ms 
    socket.io-parser encoded {"type":2,"data":["progress",{"progress":99.93823786632886}],"nsp":"/"} as 2["progress",{"progress":99.93823786632886}] +0ms 
    engine:socket sending packet "message" (2["progress",{"progress":99.93823786632886}]) +0ms 
    engine:socket flushing buffer to transport +0ms 
    engine:ws writing "42["progress",{"progress":99.84186540937002}]" +0ms 
    engine:ws writing "42["progress",{"progress":99.93823786632886}]" +0ms 

我在做什麼錯在這裏?有沒有不同的方式將事件從服務器發送到我只缺少特定的客戶端?

回答

3

您發佈的代碼的第二個示例應該可以正常工作,如果不是,則應該發佈更多的代碼。

據我所知,一個插座連接到上 「連接」默認室,這是由在客戶端上的「id」鏡像。 根據官方文檔,使用socket.to(id).emit()應該將 的數據範圍僅發送給該客戶端,但這不適用於我。

Socket.io比這要簡單得多。下面的代碼會發送一個「你好」的消息時,他們連接的每個客戶端:

io.sockets.on('connection', function (socket) { 
    socket.emit('hello'); 
}); 

每當一個新的客戶端連接到插座。io服務器,它將使用該特定套接字作爲參數運行指定的回調。 socket.id只是識別該套接字的唯一代碼,但您並不真的需要該變量,上面的代碼顯示如何通過特定的socket發送消息。

Socket.io還爲您提供了一些函數來創建命名空間/房間,所以你可以在某些標識符(房間名)組的連接,並能夠發送廣播消息到所有的人:

io.sockets.on('connection', function (socket) { 
    // This will be triggered after the client does socket.emit('join','myRoom') 
    socket.on('join', function (room) { 
     socket.join(room); // Now this socket will receive all the messages broadcast to 'myRoom' 
    }); 
... 

現在你應該明白socket.join(socket.id)只是沒有意義,因爲沒有套接字將共享套接字ID。

編輯回答用新的代碼問題:

這裏有兩個問題,第一:

socketConnection.upload = function upload (data) { 
     socket.to(socket.id).emit('progress', {progress:(data.progressAmount/data.progressTotal)*100}); 
    }; 

注意,在這一切上面的代碼裏面io.sockets.on('connection',function (socket) {將每一個客戶端連接時運行到服務器。您正在覆蓋該功能,將其指向最新用戶的套接字。

另一個問題是您沒有鏈接套接字和s3操作。以下是將socket.jss3upload.js合併到同一個文件中的解決方案。如果你真的需要保留它們分開,你將需要找到一種不同的方式連接套接字連接到S3操作:

var config = require('../config/aws.json'); 
var s3 = require('s3'); 
var path = require('path'); 
var fs = require('fs'); 
var Busboy = require('busboy'); 
var inspect = require('util').inspect; 
var io = require('socket.io'); 

var socketConnection = exports = module.exports = {}; 
var S3Upload = exports = module.exports = {}; 

io = socketio.listen(app); 
exports.sockets = io.sockets; 

io.sockets.on('connection', function (socket) { 

    socket.on('disconnect', function(){ 
     console.log("device "+socket.id+" disconnected"); 
    }); 

    socket.on('upload', function (data) { //The client will trigger the upload sending the data 
     /* 
      some code creating the bucket params using data 
     */ 
     S3Upload.upload(params,this); 
    }); 
}); 

S3Upload.upload = function upload(params,socket) { // Here we pass the socket so we can answer him back 
    // start uploading to uploader 
    var uploader = client.uploadFile(params); 

    uploader.on('error', function(err) { 
     console.error("There was a problem uploading the file to bucket, either the params are incorrect or there is an issue with the connection: ", err.stack); 
     res.json({responseHTML: "<span>There was a problem uploading the file to bucket, either the params are incorrect or there is an issue with the connection. Please refresh and try again.</span>"}); 
     throw new Error(err); 
    }), 

    uploader.on('progress', function() { 
     socket.emit('progress', {progress:(uploader.progressAmount/uploader.progressTotal)*100}); 
    }), 

    uploader.on('end', function(){ 
     S3Upload.deleteFile(params.localFile); 
    }); 
}; 
+0

我編輯了我的問題以顯示更多底層代碼體系結構。我不能簡單地發送連接,因爲我將來自另一個路由的信息傳遞給套接字。 –

+0

我已經根據你的代碼編輯了我的答案,我在同一個文件中合併了'socket.js'和's3upload.js',以使它更容易。如果你真的需要保持它們分開,我建議你切換具有導入S3的socket.js的依賴項,而不是反過來。您將需要基於套接字事件調用函數,以便它需要訪問's3upload.js'函數。 –

+0

我會嘗試用您的建議重新構建我的應用程序,感謝您的幫助。根據你的建議移動我的代碼的相關問題:是否真的,使用套接字時,應該真的圍繞着socket.io設計,以便所有的邏輯都通過套接字處理程序傳遞?如果這是真的,那麼使用套接字時不會使專用路由無法寫入? –

1

根據文檔所有用戶加入由套接字標識標識的default room,因此不需要您加入連接。仍然根據這一點,如果您想從特定套接字發送到名稱空間中的房間,您應該使用socket.broadcast.to(room).emit('my message', msg),因爲您要將消息廣播到連接到該特定房間的所有客戶端。

+0

更新我的代碼是給我同樣的問題。它運行解析器,但不運行引擎或寫入流。我運行的是socket.io 1.3.6,所以這些函數是明確定義的。讓我知道如果看到更多的代碼會有所幫助,或者如果有一種方法可以進一步調試,我不會嘗試。 –

1

所有新的連接會自動加入房間具有等於他們的插座名。ID。您可以使用它將消息發送給特定用戶,但您必須知道與此用戶初始化的連接相關的socket.id。你必須(由具有一個數組通過數據庫,或內存)來決定你將如何管理這種關聯,但一旦你擁有了它,只需發送進度百分比通過:

socket.broadcast.to(user_socket_id).emit("progress", number_or_percent); 
相關問題