How to get files from AWS S3 and zip them on the client?

I’m storing all of my images on S3 and am having difficulty allowing a user to download the images. When I upload an image (with lepozepo:s3) I create a new document in the Photos collection with the url of the image on S3 so that the images can be used as profile images/etc… on the site. I can upload to S3 and remove on S3 just fine, however, I cannot seem to figure out how to get the file data to the client in order to zip with JSZip. Here is what I have so far:

This is a meteor method that I was hoping to call on the client in order to get the files for me to zip.

getPhotos: function (prefix) {
    if (Meteor.isServer) {

        var fs = Npm.require('fs');
        var s3 = new AWS.S3();

        var params = {
            Bucket: Meteor.settings.awsBucket,
            Prefix: prefix
        };

        s3.listObjects(params, Meteor.bindEnvironment(function(err, data) {
            if (err) {
                throw new Meteor.Error("S3 List Objects Error", err);
            } else {
                data.Contents.forEach(function(photo) {
                    s3.getObject({Bucket: Meteor.settings.awsBucket, Key: photo.Key}, function(err, data) {
                        if (err) {
                            console.log(err, err.stack);
                        } else {
                            // what do I do with this data?
                        }
                    });

                });

            }
        }));

    }


}

My thought was that maybe I need to store the data in a temp images collection and then publish it to the client? However, I’m not sure how to store a buffer (I believe s3.getObject returns a buffer in the form of data.Body) in MongoDB. Even if I stored a buffer in MongoDB I don’t even know what I’d do with it on the client side to turn it into an image.

I’m realizing very quick that I don’t know much about how data works in Node haha. Any help would be appreciated.

There was similar thread and they were creating server side route with :imageId which was random generated string currently paired to requested image in collection.
That route was just proxying connection to s3 and returning raw image/data .

@shock That seems like that’s what I’ll have to do. I was trying to also generate a PDF on the client but then resorted to generating the PDF on the server and sending it to the client with a server side route.

Any news about how to do it?

1 Like

I’m in the same pickle… Any thoughts here? I’ve been playing around with this for a few hours. It seems like I can’t scope my

s3.listObjects(params, (err, data) => {
      if (err) return
      else {
        return data
      }
    })

code. I’ve tried wrapping in a promise, but no luck - on both client and server. In my else block I plan to return actual logic… but it’d to get something passed over to client.

I can’t see why this wouldn’t work. Can you share the code you used to do this?

I have it working! I had to do it Client-side. I’m using React. For anyone else that may be having this issue:

import AWS from 'aws-sdk'
import 'aws-sdk/lib/browser_loader'
import securityKeys from '../../../private'
import { DEFAULT_ROOT } from '../constants'

export const getAlbum = ({Prefix, name, event, album}) => {
//Album form me is an empty object || {}
  if (!name && event) return album
  if (name) Prefix = Prefix.concat(meetName)
  else if (name && event) Prefix = Prefix.concat(name).concat(event)

  const params = { Bucket: 'my-bucket', Prefix }
  const {accessKeyId, secretAccessKey} = securityKeys

  AWS.config = { accessKeyId, secretAccessKey }

  const s3 = new AWS.S3()

  s3.listObjects(params, (err, data) => {
    if (err) Meteor.throw(err.reason)
    else {
      const {Contents} = data
      Contents.map(medium => (medium.url = `${DEFAULT_ROOT}/${medium.Key}`))
      album = Object.assign(album, {[name]: Contents})
    }
  })
  return album
}

Then call getAlbum(param1, param2, param3, param4) in ComponentWillMount. No server-side or Meteor.call needed.