2012-12-20 10 views
8

Sto provando a scrivere un server di immagini che utilizza node.js per memorizzare immagini su s3. Il caricamento dell'immagine funziona bene, e posso scaricarlo e visualizzarlo correttamente usando un client browser s3 (sto usando dragondisk, in particolare, ma l'ho scaricato con successo anche con altri), ma quando lo scarico con il nodo e provo per scriverlo su disco, non riesco ad aprire il file (si dice che potrebbe essere danneggiato o utilizzare un formato file che Preview non riconosce). Sto usando il sdk amazon per node e fs per scrivere il file. So che puoi passare una codifica opzionale a fs.writeFile, ma li ho provati tutti e non funziona. Ho anche provato a impostare ContentType su putObject e ResponseContentType su getObject, così come ContentEncoding e ResponseContentEncoding (e tutte queste cose in varie combinazioni). Stesso risultato Ecco po 'di codice:Salvare un'immagine memorizzata su s3 usando node.js?

var AWS = require('aws-sdk') 
    , gm = require('../lib/gm') 
    , uuid = require('node-uui') 
    , fs = require('fs'); 

AWS.config.loadFromPath('./amazonConfig.json'); 
var s3 = new AWS.S3(); 

var bucket = 'myBucketName'; // There's other logic here to set the bucket name. 

exports.upload = function(req, res) { 
    var id = uuid.v4(); 
    gm.format("/path/to/some/image.jpg", function(format){ 
     var key = req.params.dir + "/" + id + "/default." + format; 
     fs.readFile('/path/to/some/image.jpg', function(err, data){ 
      if (err) { console.warn(err); } 
      else { 
       s3.client.putObject({ 
        Bucket: bucket, 
        Key: key, 
        Body: data, 
        ContentType: 'image/jpeg' 
        // I've also tried adding ContentEncoding (in various formats) here. 
       }).done(function(response){ 
        res.status(200).end(JSON.stringify({ok:1, id: id})); 
       }).fail(function(response){ 
        res.status(response.httpResponse.statusCode).end(JSON.stringify(({err: response}))); 
       }); 
      } 
     }); 
    }); 
}; 

exports.get = function(req, res) { 
    var key = req.params.dir + "/" + req.params.id + "/default.JPEG"; 
    s3.client.getObject({ 
     Bucket: bucket, 
     Key: key, 
     ResponseContentType: 'image/jpeg' 
     // Tried ResponseContentEncoding here in base64, binary, and utf8 
    }).done(function(response){ 
     res.status(200).end(JSON.stringify({ok:1, response: response})); 
     var filename = '/path/to/new/image/default.JPEG'; 
     fs.writeFile(filename, response.data.Body, function(err){ 
      if (err) console.warn(err); 
      // This DOES write the file, just not as an image that can be opened. 
      // I've tried pretty much every encoding as the optional third parameter 
      // and I've matched the encodings to the ResponseContentEncoding and 
      // ContentEncoding above (in case it needs to be the same) 
     }); 
    }).fail(function(response){ 
     res.status(response.httpResponse.statusCode).end(JSON.stringify({err: response})); 
    }); 
}; 

Per inciso, io sto usando espresso per il routing, così che è dove req.params proviene.

+0

Ho appena scoperto che se passo 'base64' come codifica a fs.readFile() e fs.writeFile(), è vero il contrario. Posso scaricare e visualizzare l'immagine usando il mio servizio, ma NON posso visualizzarlo se lo scarico utilizzando un client browser s3. C'è un modo per fare entrambe le cose? – tandrewnichols

risposta

5

Ok, dopo prove ed errori significativi, ho capito come fare. Ho finito per passare a Knox, ma presumibilmente, è possibile utilizzare una strategia simile con aws-sdk. Questo è il tipo di soluzione che mi fa dire "Ci deve essere un modo migliore di questo", ma sono soddisfatto di tutto ciò che funziona, a questo punto.

var imgData = ""; 
client.getFile(key, function(err, fileRes){ 
    fileRes.on('data', function(chunk){ 
     imgData += chunk.toString('binary'); 
    }).on('end', function(){ 
     res.set('Content-Type', pic.mime); 
     res.set('Content-Length', fileRes.headers['content-length']); 
     res.send(new Buffer(imgData, 'binary')); 
    }); 
}); 

getFile() rendimenti blocchi di dati come buffer. Si potrebbe pensare che si potrebbero semplicemente collegare i risultati direttamente al front end, ma per qualsiasi motivo, questo era l'UNICO modo in cui potevo ottenere il servizio per restituire un'immagine correttamente. È ridondante scrivere un buffer su una stringa binaria, solo per riscriverlo in un buffer, ma hey, se funziona, funziona. Se qualcuno trova una soluzione più efficiente, mi piacerebbe sentirla.

+0

S3 si aspetta la lunghezza del flusso. Detto questo, è qualcosa di un hack, ma se si imposta la proprietà .length sul flusso che si sta passando al corpo S3 sulla chiamata putObject, ciò avrebbe fatto il trucco. –

10

Per le persone che stanno ancora lottando con questo problema. Ecco l'approccio che ho usato con nativo aws-sdk.

var AWS = require('aws-sdk'); 
AWS.config.loadFromPath('./s3_config.json'); 
var s3Bucket = new AWS.S3({ params: {Bucket: 'myBucket'} }); 

all'interno del vostro metodo di router: - ContentType dovrebbe essere impostato il tipo di contenuto del file di immagine

buf = new Buffer(req.body.imageBinary.replace(/^data:image\/\w+;base64,/, ""),'base64') 
    var data = { 
    Key: req.body.userId, 
    Body: buf, 
    ContentEncoding: 'base64', 
    ContentType: 'image/jpeg' 
    }; 
    s3Bucket.putObject(data, function(err, data){ 
     if (err) { 
     console.log(err); 
     console.log('Error uploading data: ', data); 
     } else { 
     console.log('succesfully uploaded the image!'); 
     } 
    }); 

file di s3_config.json è: -

{ 
    "accessKeyId":"xxxxxxxxxxxxxxxx", 
    "secretAccessKey":"xxxxxxxxxxxxxx", 
    "region":"us-east-1" 
} 
Problemi correlati