I am trying to upload files to an S3 endpoint from a static HTML page but the files are always malformed when I download them from the bucket. The relevant code is below - what am I doing wrong with fetch?
const onSubmitForm = function (e) {
const file = this.files[0];
const reader = new FileReader();
// reader.readAsText(file); // didn't work
reader.readAsDataURL(file); // also didn't work
reader.onload = async function () {
const bodyData = {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
file: {
// the backend endpoint expects a base64 encoded img
// the upload completes but the
"data": reader.result.toString()
},
"name": file.name
})
}
const response = await fetch(uploadUrl, bodyData).then(
res => res.json()
);
};
reader.onerror = function (error) {
console.log('Error: ', error);
};
//
}
Related
I am trying to upload an image from my react native app.
If I use my local node server and run this code:
var fs = require("fs");
var options = {
method: "POST",
url: "my_URL",
headers: {},
formData: {
file: {
value: fs.createReadStream("../../assets/image.png"),
options: {
filename: "image.jpg",
contentType: null
}
}
}
};
request(options, function (error, response) {
if (error) throw new Error(error);
console.log(response.body);
});
I have a succesful upload.
However, with the URI from that we get through the the app, it does not work:
Here is my code on React Native Expo:
const body = new FormData();
body.append("file", 'file:///path/to/file/image123.jpg');
fetch(url, {
method: "POST",
body,
headers: {
"content-type": "multipart/form-data"
}
})
.then(response => {
console.log(response, "RESPONSE");
})
.then(result => {
console.log(result, "RESULT");
})
.catch(error => {
console.log(error, "ERROR");
});
I am unable to get it to work. I think it has something to do with the file path from the device.
Any help will be appreciated.
try to create FormData using this function
const createFormData = (uri) => {
const fileName = uri.split('/').pop();
const fileType = fileName.split('.').pop();
const formData = new FormData();
formData.append('file', {
uri,
name: fileName,
type: `image/${fileType}`
});
return formData;
}
if it doesn't work check permissions
I'm trying to send an image from react native to my server using fetch and it's working on ios but not android. i'm testing on physical devices. the error that is returned is a Network Error exception.
Also, I'm using fetch for all of my api calls, and all the POST requests where I'm sending just a JSON body are working fine, even on Android. It's just sending the image using fetch + formData that's not working on Android.
Some things I've tried are (mostly suggestions on other questions similar to this one)
[commenting out flipper in MainApplication][1]: https://stackoverflow.com/a/61126831/2395829
tried working with the XMLHttpRequest object directly
tried removing the headers in the post request
added android:usesCleartextTraffic="true" to AndroidManifest.xml
I've spent a few hours on this but can't get it to work...
It's possible some of the changes I made to AndroidManifest didn't get synced. I ran npm run android after changing file and it said the Gradle sync completed so I don't think that's too likely...
The code snippet below is where the formData object is created and the fetch request sent
const data = new FormData()
data.append('avatar', {
uri: res,
type: 'image/jpeg',
name: 'gravavatar',
uid: userData.id,
imagePos: idx,
})
fetch(global.BASE_URL + '/save_profile_image/' + userData.id + '/' + imagePos + '/no', {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'multipart/form-data',
// 'Access-Control-Allow-Origin': '*',
},
// make sure to serialize your JSON body
body: data
}).then(response => {
if (response.ok) {
// do stuff like setState
}
}).catch(err => {
console.log(err)
})
The entire function is below.
const _pickImage = async (idx) => {
try {
let result = null
try {
result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.All,
allowsEditing: false,
aspect: [4, 3],
quality: 0.05,
});
} catch (err) {
console.log('IMAGE FAILED')
console.log(err)
return;
}
if (!result.cancelled) {
let images = [...state.images];
images[idx] = result.uri
if (result.uri.slice(0, 4) == 'file') {
var xhr = new XMLHttpRequest();
xhr.open("GET", result.uri, true);
xhr.responseType = "blob";
xhr.onload = function(e) {
console.log(this.response);
var reader = new FileReader();
reader.onload = async function(event) {
var res = event.target.result;
var stringLength = res.length - 'data:image/png;base64,'.length;
var sizeInBytes = 4 * Math.ceil((stringLength / 3)) * 0.5624896334383812;
var sizeInKb = sizeInBytes / 1000;
// console.log(sizeInKb)
if (sizeInKb > 4999) {
alert('File is too large')
return;
}
const data = new FormData()
data.append('avatar', {
uri: res,
type: 'image/jpeg',
name: 'gravavatar',
uid: userData.id,
imagePos: idx,
})
fetch(global.BASE_URL + '/save_profile_image/' + userData.id + '/' + imagePos + '/no', {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'multipart/form-data',
// 'Access-Control-Allow-Origin': '*',
},
// make sure to serialize your JSON body
body: data
}).then(response => {
if (response.ok) {
// do stuff like setState
}
}).catch(err => {
console.log(err)
})
}
var file = this.response;
reader.readAsDataURL(file)
};
xhr.send()
}
return;
}
} catch (E) {
console.log(E);
}
};
Help would be much appreciated.
Thank you.
In my react native project I need to be able to send Images using axios to an API. For that I have the following function:
export function SetImage(image, id, token)
{
const formData = new FormData();
formData.append('file',{
uri: image.uri,
type: image.type,
})
return axios({
method: 'PUT',
url: axios.defaults.baseURL + "/api/SetImage/"+ID,
headers: {
'Content-Type': 'multipart/form-data' ,
'Authorization': 'Bearer: '+token,
},
data: formData
})
}
Image is the return Object I got from ImagePicker.launchImageLibraryAsync function which looks something like this:
{
"cancelled": false,
"height": 2048,
"type": "image",
"uri": "file:///data/user/0/host.exp.exponent/cache/<PathtoSomewhere>/ImagePicker/1d408e33-b54a-4189-
ac66-bd86ec11069a.jpg",
"width": 946,
}
However when I try to use the function I get the following error, that doesn't tell me anything:
Network Error
at node_modules\axios\lib\core\createError.js:16:14 in createError
at node_modules\axios\lib\adapters\xhr.js:84:13 in handleError
- ... 9 more stack frames from framework internals
I recently have to add same functionality to my project (upload image trough a pre-signed URL). This one is the ones that works for me:
const uploadImage = async ({ method, url, file }: any) => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.setRequestHeader('Content-Type', file.type);
xhr.onload = () => {
if (xhr.status !== 200) {
reject(
new Error(
`Request failed. Status: ${xhr.status}. Content: ${xhr.responseText
}`,
),
);
}
resolve(xhr.responseText);
};
xhr.send(file);
});
};
// image === image inside local phone
export const uploadImageToUrl = async ({ image, url }: any) => {
await uploadImage({
method: 'PUT', url, file: {
uri: image.uri,
type: image.type,
}
});
return { url };
};
To upload an image, you just need to call it like:
uploadImageToUrl({ url: your-upload-url, image: your-image-object-from-image-picker })
Note: pass the whole image object to the function (not just the uri)
Also add this line if necessary:
xhr.setRequestHeader('Authorization', 'Bearer ' + jwtoken);
I get a blob and I treat it like this:
const file = response.data;
var blob = new Blob([file], {
type: 'application/pdf',
});
const fileReaderInstance = new FileReader();
fileReaderInstance.readAsDataURL(blob);
fileReaderInstance.onload = async () => {
const fileUri = `${FileSystem.documentDirectory}file.pdf`;
await FileSystem.writeAsStringAsync(
fileUri,
fileReaderInstance.result.split(',')[1],
{
encoding: FileSystem.EncodingType.Base64,
}
);
console.log(fileUri);
Sharing.shareAsync(fileUri);
};
however when I generate and share the file, I can't access it and if I get its URI and search on the web it returns:
i solved my problem in this way:
This is a func who get other data to request, do the request (generate PDF()) and treat the data and generate by received blob the buffer on (fileReaderInstance.result) who is shared in Sharing.shareAsync()
const generatePDF = async () => {
setAnimating(true);
const companyReponse = await CompanyService.getCompany();
const peopleResponse = await PeopleService.getPerson(sale.customerId);
const company = companyReponse.response.company;
const people = peopleResponse.response;
const quote = false;
const json = await SaleService.generatePDF({
sale,
company,
people,
quote,
});
if (json && json.success) {
try {
const fileReaderInstance = new FileReader();
fileReaderInstance.readAsDataURL(json.data);
fileReaderInstance.onload = async () => {
const base64data = fileReaderInstance.result.split(',');
const pdfBuffer = base64data[1];
const path = `${FileSystem.documentDirectory}/${sale._id}.pdf`;
await FileSystem.writeAsStringAsync(`${path}`, pdfBuffer, {
encoding: FileSystem.EncodingType.Base64,
});
await Sharing.shareAsync(path, { mimeType: 'application/pdf' });
};
} catch (error) {
Alert.alert('Erro ao gerar o PDF', error.message);
}
}
setAnimating(false);
}
This is the func in SaleServicegeneratePDF who do the request to api and pass the parameters that return a blob of pdf using axios:
generatePDF: async ({ sale, company, people, quote }) => {
const token = await AsyncStorage.getItem('token');
const body = { sale, company, people, quote };
try {
const response = await axios(`${BASE_API}/generate-sale-pdf`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: token,
},
responseType: 'blob',
data: body,
});
return {
success: true,
data: response.data,
};
} catch (err) {
return err.error;
}
},
I have solved this problem by passing the blob string to WriteAsStringAsync method of FileSystem library from expo.
const blobDat = data.data[0].data; //blob data coming from an API call
const fileUri = FileSystem.documentDirectory + `testt.pdf`; //Directory Link of the file to be saved
await FileSystem.writeAsStringAsync(fileUri, blobDat, {
encoding: FileSystem.EncodingType.UTF8,
}) //This step writes the blob string to the pdf fileURI
await IntentLauncher.startActivityAsync("android.intent.action.VIEW", {
data: fileUri,
flags: 1,
type: "application/pdf",
});
//prompts user with available application to open the above created pdf.
I'm having issues uploading an image from Nativescript to AWS, and I'm pretty sure it's a configuration issue.
select an image
const context = imagepicker.create({
mode: 'single' // use "multiple" for multiple selection
});
await context.authorize();
const selection: Array<ImageAsset> = await context.present();
const imageAsset = selection[0];
const source: ImageSource = await new ImageSource().fromAsset(imageAsset);
const fileLocation = imageAsset.android ? imageAsset.android : imageAsset.ios;
const fileType = mime.extension(mime.lookup(fileLocation));
const image = source.toBase64String(fileType);
console.log(image);
image at this point is: iVBORw0KGgoAAAANSUhEUgAAB4AAAASwCAIAAACVUsChAAAAA3NCSVQI...
image location at this point is: /storage/emulated/0/DCIM/Screenshots/Screenshot_20181106-150854.png
const fileLocation = imageAsset.android ? imageAsset.android : imageAsset.ios;
const signedUrl = await this.getSignedUrl(fileLocation);
Backend Code to get signedURL
const getSignedUrlPromise = (operation, params) => {
return new Promise((resolve, reject) => {
s3.getSignedUrl(operation, params, (err, url) => {
err ? reject(err) : resolve(url);
});
});
}
const params = {
Bucket: BUCKET_NAME,
Key: `abc123/456/3/${fileName}`,
ContentType: contentType,
ContentEncoding: 'base64'
}
const url = await getSignedUrlPromise('putObject', params).catch(err => {
console.log('error', JSON.stringify(err))
return {
statusCode: 400,
body: JSON.stringify(err)
}
});
console.log('success', url);
return {
statusCode: 200,
body: JSON.stringify({ url: url })
}
signedUrl at this point is:
https://myproject.s3.amazonaws.com/abc123/456/3/Screenshot_20181106-150854.png?AWSAccessKeyId=xxxxxxxxxxxxxx&Content-Encoding=base64&Content-Type=image%2Fpng&Expires=1555517358&Signature=yyyyyyyy&x-amz-security-token=long_token
Then, using the signedURL, i upload the image:
const mimeType = mime.lookup(fileLocation);
this.http.put(signedUrl, image, {
headers: {
'Content-Type': mimeType,
'Content-Encoding': 'base64'
}
}).subscribe((resp) => {
console.log('resp2', resp);
});
}
When I open the file, this is what I see
and the meta-data on the S3 object looks correct
When I download the file and open it in NP++, I see the base64 value.
iVBORw0KGgoAAAANSUhEUgAAB4AAAASwCAIAAACVUsChAAAAA3NCSVQI...
I also cannot open the downloaded image
ATTEMPT 2
I saw where some people were using buffers, so I changed my image code to
const image = Buffer.from(source.toBase64String(fileType).replace(/^data:image\/\w+;base64,/, ''), 'base64');
which the image is still broken, and when I download and open the file using NP++ I see
{"type":"Buffer","data":[137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,7,128,0,0,4,176,8,2,0,0,0