Hi,
a few days ago I've reported some performance issues for storage.put
and I got a working solution
which will reduce the time from 30 seconds to 5 seconds.
My use case is to upload >4MB images to the S3 bucket which was taken by expo.Camera.takePictureAsync()
.
Now I have a strange issue which only occures when I publish my app via expo to make it available on expo.io.
What is the current behavior?
Upload image via local uri -> fetch -> get blob -> storage.put with blob works perfect on development mode.
const response = await fetch(uri);
const blob = await response.blob();
Storage.put(folder + '/' + filename, blob, { contentType: 'image/jpeg' })
.then(result => resolve(result))
.catch(err => reject({error: err, object: blob}));
When I upload the image in the published version I get the following error:
"error": {
"line": 1004,
"column":171051,
"sourceUrl":"https://...cloudfront.net../../../-26.0.0-ios.js",
"message": "Expected params.Body to be a string, Buffer, Stream, Blob, or typed array object",
"code": "InvalidParameterType"
},
"object": {
"_data": {
"size": 3142386,
"bloblId": "12312312312-213-123-123-123",
"type": "image/jpeg",
"offset":0,
"name": "my_pic.jpeg",
}
}
Don't know where the error comes from but this doesn't happen on the development environment. :|
My Stack
My AWSHelper helper class
import Amplify, { Storage } from 'aws-amplify';
import aws_exports from '../../../aws-exports';
import { Buffer as BufferLib } from 'buffer';
Amplify.configure(aws_exports);
Storage.configure({
bucket: "MY_BUCKET",
level: "public",
});
export default class AWSHelper {
constructor() { }
upload_with_uri(folder, filename, uri) {
return new Promise(async (resolve, reject) => {
const response = await fetch(uri);
const blob = await response.blob();
Storage.put(folder + '/' + filename, blob, { contentType: 'image/jpeg' })
.then(result => resolve(result))
.catch(err => reject({error: err, object: blob}));
});
}
}
My bucket configuration
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>HEAD</AllowedMethod>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<AllowedMethod>POST</AllowedMethod>
<AllowedMethod>DELETE</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<ExposeHeader>x-amz-server-side-encryption</ExposeHeader>
<ExposeHeader>x-amz-request-id</ExposeHeader>
<ExposeHeader>x-amz-id-2</ExposeHeader>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
my aws-exports.js
const awsmobile = {
'aws_app_analytics': 'enable',
'aws_cognito_identity_pool_id': 'eu-central-1:XYZ',
'aws_cognito_region': 'eu-central-1',
'aws_mobile_analytics_app_id': 'XYZ',
'aws_mobile_analytics_app_region': 'us-east-1',
'aws_project_id': 'XYZ',
'aws_project_name': 'myupload-app',
'aws_project_region': 'eu-central-1',
'aws_resource_name_prefix': 'myupload-app-mobilehub-12345',
'aws_user_files': 'enable',
'aws_user_files_s3_bucket': 'myupload-app-userfiles-mobilehub-12345',
'aws_user_files_s3_bucket_region': 'eu-central-1',
}
Many thanks for any help!
Appreciate it!
Hi @MobileAppVault ,
Could you try using something similar to fs in nodejs to create a read stream (for eg: in fs, the method would be createReadStream). There are npm packages that are available for this in react-native too.
Hi @nidsharm ,
I guess I have to detach my expo app if I want to do that.
Correct me if I'm wrong?
Maybe there is some misconfiguration on my side.
I've added my aws-exports.js
and cors configuration on my bucket in my issue request.
It's strange I've also tried to get the blob with XMLHttpRequest
and it works locally as well but not when I published the app with expo. I've also have authentication in place or something else.
return new Promise(async (resolve, reject) => {
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.open('GET', uri , true);
xhr.onreadystatechange = function () {
if (xhr.readyState == xhr.DONE) {
const blob = xhr.response;
Storage.put(filename, blob,
{
bucket: "myupload-app-userfiles-mobilehub-12345",
level: "public",
contentType: 'image/jpeg'
})
.then(result => resolve(result))
.catch(err => reject({error: err, object: blob}));
}
};
xhr.send();
});
Hi @MobileAppVault ,
You are right, if you have to use react-native-fs package you will have to detach your app.
I would suggest you print out the value of blob in console logs before sending it, and also check if you are uploading only jpeg images here.
Ok, will give it a try the next days and let u know if this works.
I am also having this issue and would like to avoid detaching if possible. Any solution?
I am having this issue as well
Is there a workaround for this? @MobileAppVault @mlabieniec I am having the same issue. I am uploading audio files to S3. Works fine in development but not sure why after I publish it, anything is different.
I have the same issue... Uploading blob works perfectly from XDE, after publish same code throws errors.
I've found a workaround usingbase64
without detaching (using aws-sdk
, not aws-amplify
lib):
this.camera.takePictureAsync({ quality, base64: true })
.then(data => {
....
const buffer = Buffer.from(data.base64, 'base64');
s3obj.upload({
Body: buffer,
Bucket: awsConfig.awsBucket,
Key: `myImage.png`,
ContentType: 'image/png',
})
.send(callback);
I would really prefer using blob over byte64 as it's much faster (and cleaner).
I am having the same problem with trying to send audio to the Amazon Lex API. It works in development but not on the actual phone:
const response = await fetch(uri);
const blob = await response.blob();
let params = { ...
inputStream: blob,
}
await lexRuntime.postContent(params, this._handleLexResult);
console output appears the same for the blob in development and on the phone (I render it to the screen on the phone). I'm not sure the base64 workaround will work for me since I am reading the file with fetch().
Having this problem as well. Currently using the base64 workaround but would MUCH rather be able to use .blob()
I am having this problem too with the APK generated by expo, i am reading an image Uri (content:///...) with fetch and blob, how are you guys solving this problem encoding to base64 using amplify instead the aws-sdk?
Hi guys. I'm having this issue too when I publish my app to expo. Have anyone found a solution to this? @MobileAppVault
Fyi, this issue is also present in aws-appsync when complex object (file) is used.
I鈥檓 also having this issue. Works fine on development but as soon as I publish, I get the params.Body problem 馃槳馃槳
Not sure if this helps but it does work in both dev and published apps with .txt. Wonder if it鈥檚 specific to pics
Identical problem but with audio files.
The PR of aws-sdk-js #2201 is a fix for that.
Thanks, MainAero! I have tested your fix and it does work for my use case. Something similar will probably also work for others who have related problems with Blobs and other services (not AWS).
@marktorrance You're welcome. For my project this fix is no longer working. I'm confused. Can you describe how you applied my fix for your project? Had aws-sdk
as dependency in my project which yield to unexpected behavior as aws-amplify
has aws-skd
as dependency as well. Removing unnecessary aws-sdk
dependency in my package.json
solved it.
@MainAero thanks for sharing! I did not have aws-sdk as a dependency but I did tinker with the node_modules files to match your PR https://github.com/aws/aws-sdk-js/pull/2201/files to my project.
After publishing to Expo, my Storage.put()'s are now working as expected.
I know touching node_modules is hacky but I'm just really really glad this is working for me.
Most helpful comment
The PR of aws-sdk-js #2201 is a fix for that.