react-native-aws3
react-native-aws3 copied to clipboard
Upload base64
Hi i would like to be able to upload images that is in base64
format.
There is a bug in another library I'm using so i cannot get the image, but I can get the base64 representation of the image. So i would like to upload that to S3 and have it become a actual image.
Any ideas on how to do this ?
Unfortunately, I am not sure. Did you try to use this library and pass the base64 data and see if that worked?
It didn't work. Error: Could not retrieve file for contentUri
@note89 @agrass did you figure out how to upload an image in base64 format? If yes, could you please share your solution? Thanks.
@benjreinhart answers on Stack Overflow suggest using Buffer
and then uploading the file with the S3 plugin, ie:
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){
I believe the react-native-aws3 codebase uses FormData
to create a set of key/value pairs representing form fields and their values for uri
, name
and type
. It then uses XMLHttpRequest
to submit to AWS S3. I think.
Is there anyway to use a buffer object in place of the uri key and object?
When I try to upload with the current code base (nothing to do with the buffer example above) with a file object like:
let file = {
uri: `data:image/png;base64, ${base64Image}`,
name: "image.png",
type: "image/png"
}
I get the error:
Error Domain=NSCocoaErrorDomain Code=256 "The file couldn’t be opened." UserInfo={NSURL
As an update, I believe S3 POST
requires a file
object, whereas S3 PUT
does not. I have unsuccessfully tried converting the base64 format to a file object to use react-native-aws3
but get a Possible Unhandled Promise Rejection
error:
let fetchResponse = await fetch(base64image)
let buffResponse = await fetchResponse.arrayBuffer()
let file = new File([buffResponse], 'image.png', {type:'image/png'})
uploader(file, (percent, modalVisible, response) => {
if(response)
{
console.log(response)
this.buildRequest(prestart)
}
})
//uploader.js
RNS3.put(file, options).then(response => {
if (response.status !== 201)
throw new Error("Failed to upload image to S3")
callback(0, false, response)
}).progress((e) => callback(e.percent, true, false))
.catch((error) => {
let errorMessage = error.text
callback(0, false, false)
})
Next I plan on using the default aws-sdk gem to put
with a buffer body as shown in a pervious comment rather than post
with a file object. If that works I may try to submit a pull request for react-native-aws3
to support put
as well as post
.
I ended up with a working solution by using aws-sdk
instead:
const Buffer = global.Buffer || require('buffer').Buffer;
var AWS = require('aws-sdk/dist/aws-sdk-react-native');
AWS.config.update({
accessKeyId: myKeyId,
secretAccessKey: mySecretKey,
region: myRegion
})
var s3 = new AWS.S3()
buf = new Buffer(base64image.replace(/^data:image\/\w+;base64,/, ""), 'base64')
let keyPrefix = `uploads/image.png`
var params = {
Bucket: "mybucket",
Key: keyPrefix,
Body: buf,
ContentType: 'image/png',
ContentEncoding: 'base64',
ACL: 'public-read'
}
let putObjectPromise = await s3.upload(params).promise()
@MrHubble great idea! Thanks for sharing. I'm running into something similar but with audio. How did you construct the base64image
?
@bdesouza I was using a signature capture component which returned a Base64 Encoded String. That component doesn't sound like it would be any help for you with audio.
My solution with reading file, decode to ArrayBuffer and upload to s3
// @flow
import { S3 } from "aws-sdk";
import { config } from "../environments/config";
import fs from "react-native-fs";
import { decode } from "base64-arraybuffer";
// init s3 client with your credentials
const s3 = new AWS.S3();
// path = "file://...", name = "profile.jpg"
const uploadFile = async (path: string, name: string): Promise => {
try {
const base64 = await fs.readFile(path, "base64");
const arrayBuffer = decode(base64);
const params = {
Body: arrayBuffer,
Bucket: config.aws.s3.bucket,
Key: `${config.aws.s3.folder}/${name}`,
ACL: "public-read",
ContentEncoding: "utf-8",
ContentType: "binary"
};
const uploadData = await s3.putObject(params).promise();
return Promise.resolve(uploadData);
} catch (error) {
return Promise.reject(error);
}
}
Oh that's an interesting approach. I ended up going the route of creating a presigned url and then doing and xhr post to it. That way I avoided having to use fs library which isn't fully supported on the Expo
framework as yet.
//Upload File
const xhr = new XMLHttpRequest();
xhr.open('PUT', presignedUrl);
xhr.setRequestHeader('X-Amz-ACL', 'public-read');
xhr.setRequestHeader('Content-Type', fileType);
xhr.onreadystatechange = function() {
console.log(xhr.responseText);
if (xhr.readyState === 4) {
if (xhr.status === 200) {
// Successfully uploaded the file.
console.log('File Uploaded!')
} else {
// The file could not be uploaded.
console.log('File upload failed. :( ')
}
}
}
xhr.send({ uri: filePath, type: fileType, name: fileName });
I spent two days trying to solve this issue! Thanks @frayeralex your solution did it for me 🎉
@frayeralex, I tried, but I'm getting:
{ [Error: Permission Denial: reading com.android.providers.media.MediaDocumentsProvider uri content://com.android.providers.media.documents/document/image%3A315/pexels-photo-267151.jpeg from pid=9944, uid=10168 requires android.permission.MANAGE_DOCUMENTS, or grantUriPermission()]
framesToPop: 1,
code: 'EUNSPECIFIED',
line: 11732,
column: 29,
sourceURL: 'http://localhost:8081/index.android.bundle?platform=android&dev=true&hot=false&minify=false' }
Any Idea how to fix?
https://github.com/joltup/react-native-fetch-blob/issues/59