react-native-fast-image icon indicating copy to clipboard operation
react-native-fast-image copied to clipboard

Caching image with token (Amazon S3 bucket image)

Open orcunorcun opened this issue 5 years ago • 19 comments

I am trying to cache an image from Amazon S3 bucket, but this url has lots of variables. So I want to cache the image for only first part of url. Is it possible?

for an example: https://mybucket-dev.s3.ap-northeast-1.amazonaws.com/public/user/805327c1-8a64-4f94-a044-a2f158b2d677/feed/feed_20191117011738039.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIA2DSNUBOKIKRUQDRV%2F20191116%2Fap-northeast-1%2Fs3%2Faws4_request&X-Amz-Date=20191116T174021Z&X-Amz-Expires=900&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEFIa

the first part of url always same: https://mybucket-dev.s3.ap-northeast-1.amazonaws.com/public/user/805327c1-8a64-4f94-a044-a2f158b2d677/feed/feed_20191117011738039.jpg

Can I use this part as a cache key? Thank you.

orcunorcun avatar Nov 16 '19 18:11 orcunorcun

i think you can use headers in source. haven't quite made it work yet, but you'd need to split your presigned URL and decode the reserved characters, e.g. %2F with decodeURIComponent.

junkdeck avatar Nov 20 '19 08:11 junkdeck

I did but it doesn't work. Is there any example for it?

orcunorcun avatar Nov 21 '19 01:11 orcunorcun

not that i could find, unfortunately. what does your errors say?

junkdeck avatar Nov 21 '19 07:11 junkdeck

@junkdeck There is no error. Just it doesn't cache it. :/

orcunorcun avatar Nov 21 '19 07:11 orcunorcun

how did you fetch the image using the header key? been trying at it without any luck for hours

junkdeck avatar Nov 21 '19 09:11 junkdeck

Any updates here?

roots-ai avatar Nov 26 '19 09:11 roots-ai

I have the same problem. Signed urls aren't cached. Is there any update on this?

boraikizoglu avatar Apr 04 '20 12:04 boraikizoglu

Take a look here:

https://github.com/aws-amplify/amplify-js/issues/5296#issuecomment-609059682

You can supply the Authorization header instead, and the url will remain static.

andreialecu avatar Apr 06 '20 12:04 andreialecu

Take a look here:

aws-amplify/amplify-js#5296 (comment)

You can supply the Authorization header instead, and the url will remain static.

Hi Andrei,

I checked your getS3SignedHeaders function but couldn't get how you create the signed headers. I realize you use aws-amplify and some credentials in the client side.

Our client side application just has a url like this: https://your-app.s3.eu-central-1.amazonaws.com/images/7_profileImage_1585830930973.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIA5K5BXRHZTYRI6JJE%2F20200406%2Feu-central-1%2Fs3%2Faws4_request&X-Amz-Date=20200406T132314Z&X-Amz-Expires=86400&X-Amz-Signature=b1c080c8b720532db082ded13699c26bfc4b2540ddebaaa668488d9cdb0d1bec&X-Amz-SignedHeaders=host&response-content-type=application%2Foctet-stream

Is it possible to generate signed headers using this url?

boraikizoglu avatar Apr 06 '20 13:04 boraikizoglu

@boraikizoglu How are you generating the URL? As you can see it has an expiration date tied to it. Storing signed URLs in a database is not recommended.

The link I added shows a solution that takes advantage of Amazon Cognito based temporary credentials -> the signature is safe to be generated in the React Native app because the accesskey/secretkey/sessiontoken are tied to the currently logged in Cognito user. So, they're temporary and tied to the current user. AWS Amplify makes this easy.

If you cannot generate signatures client side one possible workaround is to set up an API function on your server which returns the set of signed headers based on server side AWS credentials, and then use those in your app to do the request.

andreialecu avatar Apr 06 '20 14:04 andreialecu

@andreialecu, we don't store any signed URL in the database. They are generated in the server side each time user requests an image.


I finally managed to solve this issue.

Instead of sending pre-signed urls, like @andreialecu mentioned, send Authorization headers which are created by your server. I wrote a function which uses aws4 package to create signed headers. Hope this helps somebody.

import aws4 from 'aws4';

export function getURIWithSignedHeaders(imagePath) {
    if(!imagePath){
        return null;
    }
    const expires = 86400; // 24 hours
    const host = `${process.env.YOUR_S3_BUCKET_NAME}.s3.${process.env.YOUR_S3_REGION}.amazonaws.com`;
    // imagePath should be something like images/3_profileImage.jpg
    const path = `/${imagePath}?X-Amz-Expires=${expires}`;
    const opts = {
        host,
        path,
        headers: {
            'Content-Type': 'image/jpeg'
        }
    };
    const { headers } = aws4.sign(opts, {accessKeyId: process.env.YORU_ACCESS_KEY_ID, secretAccessKey: process.env.YOUR_SECRET_ACCESS_KEY});
    return {
        uri: `https://${host}${path}`,
        headers: {
            Authorization: headers['Authorization'],
            'X-Amz-Content-Sha256': headers['X-Amz-Content-Sha256'],
            'X-Amz-Date': headers['X-Amz-Date'],
            'Content-Type': 'image/jpeg',
        }
    }
}

boraikizoglu avatar Apr 06 '20 18:04 boraikizoglu

Hi @boraikizoglu, is it working properly?

randalb1991 avatar Jan 02 '21 20:01 randalb1991

@randalb1991 it worked fine for me.

boraikizoglu avatar Jan 02 '21 20:01 boraikizoglu

Hi, I tried the suggested solutions but nothing worked to me. Does anyone have another solution that worked?

lucianojsjr avatar Jan 21 '22 00:01 lucianojsjr

Using the Authorization headers works with S3 signed URLs but I am using CloudFront which does not support inclusion of the signature info inside of Authorization headers. If you're using S3 directly (not through CloudFront) see here: https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-auth-using-authorization-header.html

solarisn avatar Jan 25 '22 23:01 solarisn

I am still dealing with this issue. I am using boto3 (in Python) on my backend, and I can't seem to find anything that generates the headers mentioned by boraikizoglu. If anyone could point me in the right direction, it would be much appreciated.

scornz avatar Mar 24 '22 14:03 scornz

aws4 and react-native-aws4 are old packages.

How to do this with @aws-sdk/signature-v4 ?

roots-ai avatar Jun 20 '22 11:06 roots-ai

@roots-ai You can use whatever SDK you want to generate the URL signature on your server. You can make caching work once delivering the signed URL to your client by pulling the signature out of the query/URL parameters and including it in the Authorization header like this: https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-auth-using-authorization-header.html

If you're using CloudFront signed URLs (instead of S3 directly) you can include the signature in a Cookie header instead: https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-signed-cookies.html

solarisn avatar Jun 20 '22 15:06 solarisn

This repo caters for the above issue but still needs work: https://github.com/georstat/react-native-image-cache

dragonmaster784 avatar Mar 25 '24 13:03 dragonmaster784