S3 file upload - is slingshot still usable ? alternatives?

I’m having some troubles on an app to manage files upload to a s3 bucket (images, videos, pdf…) and I don’t seem to find a general agreement on which package to use ?

Some packages like s3 slingshots seem to be completly abandonned. Are they still usable and safe ? can they manage signedUrl ?

Is there any viable alternative to upload from client with keeping a server side control of who and what can be sent ?

Any code example would be more than welcome.

1 Like

It is usable. The biggest issue with it is it doesn’t support multi part upload. It’s possible to implement this yourself, but it’s a massive pain. And the aws documentation isn’t great. We’ve also found that small network issues cause it to crash with random errors, the advantage of multi part uploads is you can retry a chunk of the file if it fails.

Regarding signed URL I believe this is what s3 uses by default. A request is sent to your server which validated the request and returns the upload url which is signed.

Thanks for your quick feedback.

And did you find an alternative solution for the multi parts upload or could you share the integration of it into slingshot if you succeeded?

1 Like

Hi @ivo

I’m using minio and minio client to upload files, the client can upload to s3 buckets, and it’s popular and supported.

I have also used tusd in that past which supports multi-part file upload and other features.

1 Like

I’m using slingshot. It works great.

Check evaporatejs. It can do multipart-upload from the client to s3. You just need to create your own method to sign the authorization url

3 Likes

Seems nice, would you have an example of integration with Meteor to control the right or not to upload files for examples?

Hi, this minio client looks nice too. Any example of integration with Meteor in order to control the right for users to upload or not for example ? or limit file type?

You can control this on the method signing the url for authorization. If the user has no permission, do not sign the url, which means the user cannot upload to your s3 bucket

1 Like

I suggest you create a method on the server to use minio client to upload files to S3, this way you can protect your keys.

The workflow would be as follows:

  • Create client function to handle file upload and get the base64 for the selected file, then pass it to the server via Meteor.call

  • Create a server method and use minio client to handle the upload.

1 Like

Sign uploads on the server and upload from the client: https://github.com/activitree/s3up-meta

1 Like

thanks for the clarification. If we are passing the base64 to the server doesn’t it defeat the purpose of direct upload from client ?

You can definitely upload from the client, but your AWS keys will be exposed to the public, if you’re fine with that, then go a head.

When you publish your project your code will be minified and your keys will be hard to spot.

I’m working on project designed for < 50 users therefore that was my approach.

Another way, Maybe what you want is to call method to the server to generate a pre-signed URL the is short lived and can be used by the client to upload directly.

1 Like

Can’t afford to display aws key, especielly for file upload, and I would really advice you not doing so whatever the project size is.

Things can get very costly :slight_smile:

Other solution are great as they use the aws key to get the signedUrl on server side, give it to client who can then upload. So I’ll go for one of the other proposed solution I guess. Thanks a lot though!

2 Likes

Your package could do the trick as well I guess, I think you mentioned it on stack overflow as well. It’s just a bit worrying always to use a package with so little “fame” to work on a quite big project, it always feel less safe even though it may be a mistake to think so. I’ll have a look at it too.

Also I think you just publish it on npm.

Hi @rjdavid,

I’ll try implementing it in the next few days, though the documentation is quite lite (API is there but not much example) and I’m not sure what method to use where. I may need your help in the near future but I’ll explain my progress here and if it succeeds write a little “how to meteor / evaporateJs”. If you have a mini sample code of what method you use where (create to get the signedUrl on the serve after you use the config on server as well ? passing it back to client and then add to upload the file on client side using the signedurl ? ). It surely would ease the process :smiley:

Sorry but I do not have a mini sample code. Our code is part of a more complex component handling all our uploads. But the process should be simple. When you load the component for file select, you also prepare the config for evaporatejs. During this time, you send the signing parameters to the server, server sign it, and you include the signed url to the parameters of the function

Did a quick check and I was able to extract this:

getSignature = (signParams, signHeaders, stringToSign, signatureDateTime) => {
    return new Promise((resolve, reject) => {
      const inputs = {
        stringToSign,
        signatureDateTime
      };

      methods['aws.get.signed.url'].call(inputs, (error, result) => {
        if (error) {
          reject(error);
        } else if (result) {
          resolve(result);
        }
      });
    });
  };

When creating the Evaporate instance, there is a customAuthMethod wherein you pass this function

Here is the sample server code to create the signature

const hmac = (key, string) => {
          const chmac = crypto.createHmac('sha256', key);
          chmac.end(string);
          return chmac.read();
        };

        const { stringToSign, signatureDateTime } = params;

        const accessKey = Meteor.settings.private.s3.awsSecretKey;
        const dateStamp = signatureDateTime.substr(0, 8);
        const regionName = Meteor.settings.public.s3.awsRegion;
        const serviceName = 's3';

        const kDate = hmac(`AWS4${accessKey}`, dateStamp);
        const kRegion = hmac(kDate, regionName);
        const kService = hmac(kRegion, serviceName);
        const kSigning = hmac(kService, 'aws4_request');

        const signature = hmac(
          kSigning,
          decodeURIComponent(stringToSign)
        ).toString('hex');

        return signature;
2 Likes

Sounds great, thanks a lot for your effort. So if my understanding is correct (sorry if silly question but this process is new to me)

  1. you get a signedUrl from your server (using a meteor method) when loading the component (your getSignature function)
  2. you upload to s3 from the client using evaporateJs and the signedUrl you got back from the server.

Question:
What can’t we use the aws-sdk to create the signedUrl on server side ? your hmac function is having a similar role ?

Unaware of a similar function in the sdk. Inform me if there is one so that we can use that instead