2015-11-18 10 views
6

Sto utilizzando Node.js per creare un microservizio di caricamento dei media. Questo servizio funziona registrando i dati binari del caricamento in un buffer e quindi utilizzando il pacchetto S3 npm per caricare su un bucket S3. Sto cercando di utilizzare l'eventEmitter presente in quel pacchetto che mostra la quantità di dati caricati su S3, e di inviarlo al client che sta facendo il caricamento (in modo che possano vedere i progressi del caricamento). Sto usando socket.io per questo invio di dati di avanzamento al client.Socket.io Impossibile emettere i dati nella stanza univoca del cliente

Il problema che sto avendo è che l'evento .emit in socket.io invierà i dati di avanzamento del caricamento a tutti i client connessi, non solo al client che ha avviato il caricamento. A quanto ho capito, un socket si connette a una stanza predefinita su 'connection', che è specchiata dall''ID 'sul lato client. Secondo i documenti ufficiali, l'uso di socket.to (id) .emit() dovrebbe inviare i dati con scope solo a quel client, ma questo non funziona per me.

codice Esempio AGGIORNAMENTO:

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); 
}); 
}; 

Quando si utilizza DEBUG = * node myapp.js, vedo il socket.io-pa rser prendendo in queste informazioni, ma non emette al cliente:

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 

Tuttavia, se mi tolgo la parte .to di questo codice, invia i dati al client (sia pure a tutti i clienti, che non aiuterà affatto):

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

debug = * myapp.js nodo:

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 

che cosa sto facendo male qui? C'è un modo diverso di emettere eventi dal server solo a client specifici che mi mancano?

risposta

3

Il secondo esempio di codice che hai pubblicato dovrebbe funzionare e, in caso contrario, dovresti pubblicare più codice.

quanto mi risulta, una presa collega ad una camera predefinita 'connessione', che si riflette dal 'id' sul lato client. Secondo i documenti ufficiali, usando socket.to (id) .emit() dovrebbe inviare i dati con lo scope solo a quel client, ma questo non funziona per me.

Socket.io è molto più semplice di così. Il seguente codice invierà un messaggio di 'Ciao' a ciascun client quando si connettono:

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

Ogni volta che un nuovo client si connette al socket.io server, eseguirà il callback specificato usando quel particolare socket come parametro. socket.id è solo un codice univoco per identificare quel socket ma non hai davvero bisogno di quella variabile per nulla, il codice sopra mostra come inviare un messaggio attraverso un particolare socket.

Socket.io fornisce anche alcune funzioni per creare spazi dei nomi/camere in modo da poter connessioni di gruppo sotto qualche identificatore (nome della camera) e in grado di trasmettere messaggi a tutti loro:

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' 
    }); 
... 

Ora si dovrebbe capire socket.join(socket.id) non ha senso perché nessun socket condividerà l'ID del socket.

Modifica per rispondere alla domanda con il nuovo codice:

Si hanno due problemi qui, in primo luogo:

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

Nota nel codice qui sopra che tutto all'interno io.sockets.on('connection',function (socket) { verrà eseguito ogni volta che un client si connettono al server. Stai sovrascrivendo la funzione per indirizzarla al socket dell'utente più recente.

L'altro problema è che non si collegano i socket e le operazioni s3. Ecco una soluzione che unisce socket.js e s3upload.js nello stesso file. Se si ha realmente bisogno di tenerli separati è necessario trovare un modo diverso per collegare la connessione socket per il funzionamento 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

Ho modificato la mia domanda per mostrare più dell'architettura di codice sottostante. Non posso semplicemente emettere sulla connessione, dato che sto passando le informazioni da un'altra route nel socket. –

+0

Ho modificato la mia risposta in base al codice, ho unito 'socket.js' e' s3upload.js' nello stesso file per renderlo più semplice. Se hai davvero bisogno di tenerli separati, ti consiglio di attivare la dipendenza avendo socket.js che importa S3 e non viceversa. Dovrai chiamare le funzioni in base agli eventi del socket, quindi dovrà accedere alle funzioni 's3upload.js'. –

+0

Proverò a ri-architettura la mia app con i tuoi suggerimenti, grazie per l'aiuto. Domande correlate basate sul tuo suggerimento per spostare il mio codice: è vero che quando si utilizza il socket, le app dovrebbero davvero essere progettate attorno a socket.io, in modo che tutta la logica sia passata attraverso il gestore di socket? Se ciò è vero, non renderebbe impossibile scrivere percorsi dedicati durante l'utilizzo di socket? –

1

In base alla documentazione, tutti gli utenti si uniscono allo default room identificato dall'id del socket, quindi non è necessario per partecipare alla connessione. Tuttavia, se si desidera emettere una stanza in uno spazio dei nomi da un socket specifico, utilizzare socket.broadcast.to(room).emit('my message', msg), dato che si desidera trasmettere il messaggio a tutti i client connessi a quella specifica stanza.

+0

aggiornare il mio codice con che sta dando lo stesso problema È in esecuzione il parser, ma non il motore o il flusso di lavoro. Sto usando socket.io 1.3.6, quindi queste funzioni sono definitivamente definite. Fammi sapere se vedere più del mio codice potrebbe essere d'aiuto, o se c'è un modo per eseguire il debug di ciò che non sto provando. –

1

Tutti i nuovi collegamenti si aggiungono automaticamente alla camera ha il nome che è pari al loro presa .id. È possibile utilizzarlo per inviare messaggi a un utente specifico, ma è necessario conoscere socket.id associato alla connessione inizializzata da questo utente. Dovete decidere come si intende gestire questa associazione (tramite i database, o nella memoria avendo una matrice per esso), ma una volta che lo avete, basta inviare percentuale di avanzamento tramite:

socket.broadcast.to(user_socket_id).emit("progress", number_or_percent); 
Problemi correlati