React-Native Populate image with URL that has been converted from a blob - fetch-api

I am trying to populate an image with a URl
<Image source={{uri: this.state.imageURL}} style={styles.thumb} />
I request the image from the server, using the fetch API, and it returns a blob.
I then convert the BLOB into a URL using the following line:
var imageURL = window.URL.createObjectURL(attachmentBLOB);
When I console.log() the imageURL it prints:
blob:http%3A//localhost%3A8081/4ce24d92-0b7e-4350-9a18-83b74bed6f87
I am getting no errors or warning. The image is just not displaying
I am using the android emulator.
Please help. Thanks in advance!

Solution
React-Native does not support blobs [ref: Git/React-Native]. In order to get this working I had to download react-native-fetch-blob which returns a base64 string.
Function that returns base64 string:
var RNFetchBlob = require('react-native-fetch-blob').default;
getImageAttachment: function(uri_attachment, mimetype_attachment) {
return new Promise((RESOLVE, REJECT) => {
// Fetch attachment
RNFetchBlob.fetch('GET', config.apiRoot+'/app/'+uri_attachment)
.then((response) => {
let base64Str = response.data;
var imageBase64 = 'data:'+mimetype_attachment+';base64,'+base64Str;
// Return base64 image
RESOLVE(imageBase64)
})
}).catch((error) => {
// error handling
console.log("Error: ", error)
});
},
Populate Image with base64
I then populate the image withe the returned base64Image with:
<Image source={{uri: imageBase64}} style={styles.image} />

Related

Flutter S3 download using presigned URL issue

I am attempting to use presigned urls to download files on s3 to my flutter app. I have a lambda function that generates and passes the url. The presigned url works fine by itself but once it gets into flutter the data is somehow changed and AWS comes back with 404 error. It seems that somehow the token is corrupted.
If have tried parsing the data returned as XML, JSON and with no parsing what so ever. I have also changed the output from the lambda to be JSON or just send the url directly, neither solved the issue. None of these approaches have worked. Do I have to extract XML or something?
Here's the code that gets the url from a lambda call:
http.Response res1 = await http.get(url2);
dynamic data1 = cnv.jsonDecode(res1.body); //XmlDocument.parse(res1.body);
if (data1['theStatus'] == "error") {
String theStatus2 = "error";
return theStatus2;
} else {
(data1['theUrl']);
writeData(77, data1['theUrl']); //save in Hive
return data1;
}
Here's the code that uses the presigned url:
tFuture<File?> downloadFile(String url, String name) async {
final appStorage = await getApplicationDocumentsDirectory();
final file = File('${appStorage.path}/$name');
final response = await Dio().get(
url,
options: Options(
responseType: ResponseType.bytes,
followRedirects: false,
receiveTimeout: 0,
),
);
final raf = file.openSync(mode: FileMode.write);
raf.writeFromSync(response.data);
print('file saved');
await raf.close();
return file;
}
http.Response res1 = await http.get(url2);
final data1 = cnv.jsonDecode(res1.body); //XmlDocument.parse(res1.body);
if (data1['theStatus'] == "error") {
String theStatus2 = "error";
return theStatus2;
} else {
String theUrl = data1['theUrl'];
writeData(77, data1['theUrl']); //save in Hive
return theUrl;
}
If I hard code theUrl above with a presigned url from the browser accessing the lambda everything works fine...
I believe the issue is something to do with XML but when I use XML parse it throws an error no root... Any help appreciated.
Found the issue... my lambda function did not have the correct credentials. I found the solution using this:
How I can generated pre-signed url for different file versions of AWS S3 objects in NodeJS?

image picker path for stripe readfilesync Flutter Web

I'm using the file_picker package for flutter https://pub.dev/packages/file_picker
I have read many times that because you can’t access paths on web browsers, you need to use the bytes property, e.g.
FilePickerResult result = await FilePicker.platform.pickFiles();
if(result != null) {
var path = print(result.files.single.path); // this will return null
var bytes = print(result.files.singe.bytes); // this will return a Uint8List of bytes
} else {
// User canceled the picker
}
But I have to upload the images my users select from their devices via the web (so for all types of devices) to my Stripe Connect API in order for them to have a validated identity_document when they register. The bytes Uint8List will throw an error from firebase, here is my code:
export const uploadIdentityFront = async (uid: any, identityFront: any) => {
const fp = fs.readFileSync(identityFront);
const frontIdentity = await stripe.files.create({
file: {
data: fp,
name: 'identityFront.jpg',
type: 'application/octet-stream',
},
purpose: 'identity_document',
});
await updateId(uid, { frontIdentityFileId: frontIdentity.id })
return frontIdentity;
}
The error thrown:
[firebase_functions/unknown] TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string or an instance of Buffer or URL. Received an instance of Array
I will need to send stripe an image document via the file system's readFileSync property in order to do this, but with Flutter Web not being able to print the path for the image chosen by the user, I am stuck on how to resolve this issue
I use this code to send bytes to my server, which uses stream to send. You can use http package to send streams.
var request = http.MultipartRequest(
'POST',
Uri.parse('_url'),
);
request.files.add(
http.MultipartFile.fromBytes(
'identityFront', //name of field which you receive in api
bytes, // bytes
filename: 'identityFront.jpg', // optional name
//contentType: content, optional media Type
));
request.fields.addEntries([
MapEntry('uid', 'uid_value_in_String_Type'),
]);
await request.send();
I finally solved it. For anyone trying to upload a file to Stripe via flutter web, don't create a fs.readFileSync in your backend server side code. Instead, remove it and upload a file like this:
export const uploadIdentityFront = async (uid: any, identityFront: any) => {
const frontIdentity = await stripe.files.create({
file: {
data: identityFront,
name: 'identityFront.jpg',
type: 'image/jpg',
},
purpose: 'identity_document',
});
await updateId(uid, { frontIdentityFileId: frontIdentity.id })
return frontIdentity;
}
This way, you can upload the file via the file_picker package and uploading it as a picker.file.first.bytes. But don't wrap it in a string - send it just like this as a callable function in firebase functions:
await uploadFrontPassport.call(
<dynamic, dynamic>{'identityFront':picked.files.first.bytes}
);

Unable to format file to upload to Cloudinary

I am trying to send an image from Flutter application to Cloudinary using Uploading with a direct call to the REST API,but getting this error:
{"error":{"message":"Invalid file parameter. Make sure your file parameter does not include '[]'"}}
I tried to encode image as an array of bytes, base64Encode but none of that worked, I followed this documentation.
Does anybody know how to encode the file so I can send it?
thanks
EDIT:
#override
Future<void> getImageSignature(File image) async {
return await _callWithExceptionWrap(() async {
if (image != null) {
DateTime dateTime = DateTime.now();
String url = _formatUrlForUploadSignature();
Dio dio = NetworkUtils.createDioConnection();
debugPrint('REQUEST TO SERVER');
Response serverResponse = await dio.post(url, data: {
"paramsToSign": {
'public_id': 'public_id_654',
"timestamp": dateTime.millisecondsSinceEpoch,
"upload_preset": "signed_preset",
"source": "uw",
}
});
debugPrint('REQUEST TO CLOUDINARY');
String signature = serverResponse.data['signature'];
List<int> bytes = image.readAsBytesSync();
var base64Image = base64Encode(bytes);
Map<String, dynamic> map = {
'api_key': _CLOUDINARY_API_KEY,
'public_id': 'public_id_654',
'signature': signature,
'source': 'uw',
'timestamp': dateTime.millisecondsSinceEpoch,
'upload_preset': 'signed_preset',
'file': base64Image,
};
debugPrint('json : ${map}');
// FormData formData = new FormData.fromMap(map);
Response cloudinaryResponse = await dio.post(_CLOUDINARY_URL, data: map);
debugPrint('*************************** Cloudinary response : ${cloudinaryResponse.data}');
}
});
The signature is ok, since I am not getting 401 error(signature I am receiving from the server.
here is the cloudinary url:
_CLOUDINARY_URL = 'https://api.cloudinary.com/v1_1//image/upload';
That is not correct api to use on a public client (Mobile app), you shouldn't be exposing your API_KEY and API_SECRET. Check out this package instead, which uses the correct api to upload files https://pub.dev/packages/cloudinary_public
The package Olajide suggested would only work for Unsigned uploads. For anyone that wants to use Signed uploads use this package https://pub.dev/packages/cloudinary_sdk

Uploading images to s3 through stitch aws service fails

Sorry I am a noob, but I am building a quasar frontend using mongodb stitch as backend.
I am trying to upload an image using the stitch javascript sdks and the AwsRequest.Builder.
Quasar gives me an image object with base64 encoded data.
I remove the header string from the base64 string (the part that says "data:image/jpeg;base64,"), I convert it to Binary and upload it to the aws s3 bucket.
I can get the data to upload just fine and when I download it again I get the exact bytes that I have uploaded, so the roundtrip through stitch to aws S3 and back seems to work.
Only, the image I upload can neither be opened in S3 nor cannot be opened once downloaded.
The difficulties seem to be in the conversion to binary of the base64 string and/or in the choice of the proper upload parameters for stitch.
Here is my code:
var fileSrc = file.__img.src // valid base64 encoded image with header string
var fileData = fileSrc.substr(fileSrc.indexOf(',') + 1) // stripping out header string
var body = BSON.Binary.fromBase64(fileData, 0) // here I get the BSON error
const args = {
ACL: 'public-read',
Bucket: 'elever-erp-document-store',
ContentType: file.type,
ContentEncoding: 'x-www-form-urlencoded', // not sure about the need to specify encoding for binary file
Key: file.name,
Body: body
}
const request = new AwsRequest.Builder()
.withService('s3')
.withRegion('eu-west-1')
.withAction('PutObject')
.withArgs(args)
aws.execute(request.build())
.then(result => {
alert('OK ' + result)
return file
}).catch(err => {
alert('error ' + err)
})
In the snippet above I try to use BSON.Binary.fromBase64 for the conversion to binary as per Haley's suggestion below, but I get following error:
boot_stitch__WEBPACK_IMPORTED_MODULE_3__["BSON"].Binary.fromBase64 is not a function.
I have also tried other ways to convert the base64 string to binary, like the vanilla atob() function and the BUFFER npm module, but with no joy.
I must be doing something stupid somewhere but I cannot find my way out.
I had a similar issue, solved it by creating a buffer from the base64 data and then used new BSON.Binary(new Uint8Array(fileBuffer), 0) to create the BSON Binary Object.
Using the OP it would look something like this:
var fileSrc = file.__img.src // valid base64 encoded image with header string
var fileData = fileSrc.substr(fileSrc.indexOf(',') + 1) // stripping out header string
var fileBuffer = new Buffer(fileData, 'base64');
var body = new BSON.Binary(new Uint8Array(fileBuffer), 0)
You should be able to convert the base64 image to BSON.Binary and then upload the actual image that way (i have some of the values hard-coded, but you can replace those):
context.services.get("<aws-svc-name>").s3("<your-region>").PutObject({
Bucket: 'myBucket',
Key: "hello.png",
ContentType: "image/png",
Body: BSON.Binary.fromBase64("iVBORw0KGgoAA... (rest of the base64 string)", 0),
})

Meteor: Saving images from urls to AWS S3 storage

I am trying, server-side, to take an image from the web by it's url (i.e. http://www.skrenta.com/images/stackoverflow.jpg) and save this image to my AWS S3 bucket using Meteor, the aws-sdk meteorite package as well as the http meteor package.
This is my attempt, which indeed put a file in my bucket (someImageFile.jpg), but the image file is corrupted then and cannot be displayed by a browser or a viewer application.
Probably I am doing something wrong with the encoding of the file. I tried many combinations and none of them worked. Also, I tried adding ContentLength and/or ContentEncoding with different encodings like binary, hex, base64 (also in combination with Buffer.toString("base64"), none of them worked. Any advice will be greatly appreciated!
This is in my server-side-code:
var url="http://www.skrenta.com/images/stackoverflow.jpg";
HTTP.get(url, function(err, data) {
if (err) {
console.log("Error: " + err);
} else {
//console.log("Result: "+JSON.stringify(data));
//uncommenting above line fills up the console with raw image data
s3.putObject({
ACL:"public-read",
Bucket:"MY_BUCKET",
Key: "someImageFile.jpg",
Body: new Buffer(data.content,"binary"),
ContentType: data.headers["content-type"], // = image/jpeg
//ContentLength: parseInt(data.headers["content-length"]),
//ContentEncoding: "binary"
},
function(err,data){ // CALLBACK OF HTTP GET
if(err){
console.log("S3 Error: "+err);
}else{
console.log("S3 Data: "+JSON.stringify(data));
}
}
);
}
});
Actually I am trying to use the filepicker.io REST API via HTTP calls, i.e. for storing a converted image to my s3, but for this problem this is the minimum example to demonstrate the actual problem.
After several trial an error runs I gave up on Meteor.HTTP and put together the code below, maybe it will help somebody when running into encoding issues with Meteor.HTTP.
Meteor.HTTP seems to be meant to just fetch some JSON or text data from remote APIs and such, somehow it seems to be not quiet the choice for binary data. However, the Npm http module definitely does support binary data, so this works like a charm:
var http=Npm.require("http");
url = "http://www.whatever.com/check.jpg";
var req = http.get(url, function(resp) {
var buf = new Buffer("", "binary");
resp.on('data', function(chunk) {
buf = Buffer.concat([buf, chunk]);
});
resp.on('end', function() {
var thisObject = {
ACL: "public-read",
Bucket: "mybucket",
Key: "myNiceImage.jpg",
Body: buf,
ContentType: resp.headers["content-type"],
ContentLength: buf.length
};
s3.putObject(thisObject, function(err, data) {
if (err) {
console.log("S3 Error: " + err);
} else {
console.log("S3 Data: " + JSON.stringify(data));
}
});
});
});
The best solution is to look at what has already been done in this regard:
https://github.com/Lepozepo/S3
Also filepicker.so seems pretty simple:
Integrating Filepicker.IO with Meteor