2012-12-20 23 views
8

Próbuję napisać serwer obrazu, który używa pliku node.js do przechowywania obrazów na s3. Przesyłanie obrazu działa dobrze i mogę go pobierać i wyświetlać poprawnie za pomocą klienta przeglądarki S3 (używam dragondisk, ale udało mi się go również pobrać z innymi), ale gdy pobiorę go z węzłem i spróbuję aby zapisać go na dysk, nie mogę otworzyć pliku (mówi, że może być uszkodzony lub użyć formatu pliku, którego podgląd nie rozpoznaje). Używam amazon sdk dla węzła i fs, aby zapisać plik. Wiem, że możesz przekazać opcjonalne kodowanie do fs.writeFile, ale wypróbowałem je wszystkie i to nie działa. Próbowałem również ustawienie ContentType na putObject i ResponseContentType na getObject, a także ContentEncoding i ResponseContentEncoding (i wszystkie te rzeczy w różnych kombinacjach). Ten sam wynik. Oto niektóre kodu:Zapisywanie obrazu zapisanego na s3 przy użyciu pliku 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})); 
    }); 
}; 

Nawiasem mówiąc, używam express do routingu, tak to gdzie req.params pochodzi.

+0

Właśnie odkryłem, że jeśli przekazuję "base64" jako kodowanie do fs.readFile() i fs.writeFile(), jest odwrotnie. Mogę pobrać i wyświetlić obraz przy użyciu mojej usługi, ale NIE MOŻNA go wyświetlić, jeśli pobrałem go za pomocą klienta przeglądarki s3. Czy jest jakiś sposób robienia obu? – tandrewnichols

Odpowiedz

5

OK, po wielu próbach i błędach, zorientowałem się, jak to zrobić. Skończyło się na tym, że przeniosłem się na knox, ale przypuszczalnie można użyć podobnej strategii z aws-sdk. Jest to rodzaj rozwiązania, które sprawia, że ​​mówię: "Musi być lepsza droga niż to", ale jestem zadowolony z wszystkiego, co działa, w tym momencie.

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() zwraca porcje danych jako bufory. Można by pomyśleć, że można po prostu przeforsować wyniki prosto do przodu, ale z jakiegokolwiek powodu był to TYLKO jedyny sposób, w jaki mogłem uzyskać usługę poprawnego oddania obrazu. Uważa się za zbędne zapisywanie bufora do łańcucha binarnego, tylko zapisanie go z powrotem w buforze, ale hej, jeśli to działa, działa. Jeśli ktokolwiek znajdzie bardziej wydajne rozwiązanie, bardzo chciałbym to usłyszeć.

+0

S3 oczekuje długości strumienia. To powiedziawszy, jest to coś w stylu hackowania, ale jeśli ustawisz właściwość .length w strumieniu przekazywanym do Ciała S3 w wywołaniu putObject, to by się to udało. –

10

Dla osób, które wciąż borykają się z tym problemem. Oto podejście, z którego korzystałem w natywnym aws-sdk.

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

wewnątrz metody routera: - ContentType należy ustawić typ zawartości pliku obrazu

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

plików s3_config.json jest: -

{ 
    "accessKeyId":"xxxxxxxxxxxxxxxx", 
    "secretAccessKey":"xxxxxxxxxxxxxx", 
    "region":"us-east-1" 
} 
Powiązane problemy