how to upload images using Zendesk Upload API - react-native

I'm using the function below to upload images on zendesk. Zendesk requires image to be in binary to be uploaded and getting the blob image method is working when I'm uploading on Firebase. These function gets a response 201 but when you check the image, is just a white square. I think image got corrupted during the upload.
imageUri is returned from expo-image-picker like this below:
file:/data/user/0/host.exp.exponent/cache/ExperienceData/.../ImagePicker/8543faa5-b996-46cd-9554-ce9afb61385b.jpg
const uploadImages = async (imageUri) => {
try {
const filename = getFilenameFromImagePicker(imageUri);
const resImg = await fetch(imageUri);
const blobImg = await resImg.blob();
const response = await axios.post(`uploads?filename=${filename}`, {
blobImg
}, {
auth: {
username: membersEmail + '/token',
password: ZENDESK_API_KEY
},
headers: {
'Content-Type': 'application/binary',
}
})
return response.data;
}
catch (error) {
captureException(error);
return null;
}
}
What is the best way to change the image to binary so it can be uploaded to zendesk?
Below is the curl statement from Zendesk's documentation for uploading images
curl "https://{subdomain}.zendesk.com/api/v2/uploads?filename=myfile.dat&token={optional_token}" \
-data-binary #file.dat \
-v -u {email_address}:{password} \
-H "Content-Type: application/binary" \
-X POST

This is my typescript solution and it works.
file is file from
static uploadVideo = async (file:any) => {
// first get our hands on the local file
const localFile = await fetch(URL.createObjectURL(file));
// then create a blob out of it
const fileBlob = await localFile.blob();
const url = `${YOUR_URL}/v2/uploads?filename=${file?.name}`
return await axios({
method: "POST",
url: url,
data: fileBlob,
headers: {'Content-Type': 'application/binary'}
})
}

After alot of testing, I was able to make it work. Posting here the answer I come up with incase someone else has the same problem.
async function uploadImages (imageUri) {
// first get our hands on the local file
const localFile = await fetch(imageUri);
// then create a blob out of it (only works with RN 0.54 and above)
const fileBlob = await localFile.blob();
// then send this blob to filestack
const serverRes = await fetch('https://www.yourAwesomeServer.com/api/send/file', { // Your POST endpoint
method: 'POST',
headers: {
'Content-Type': application/binary,
},
body: fileBlob, // This is your file object
});
const serverJsonResponse = await serverRes.json();
return serverJsonResponse;
}

Related

How to use Nuxt 3 server as a passthrough API with FormData to hide external endpoints

I'm trying to get my head around the Nuxt /server API and can't seem to figure out how to send a POST request with form-data (ie files) to Nuxt server to forward on to an external service:
In my pages.vue file I have this method:
async function onSubmit() {
const formData = new FormData();
for (let file of form.files) {
await formData.append("image", file);
}
await $fetch("/api/send", {
method: "POST",
body: formData
});
}
and then in /server/api/send.js I have:
export default defineEventHandler(async (event) => {
const { method } = event.node.req;
// I THINK THE ISSUE IS HERE
const body =
method !== "GET" && method !== "HEAD"
? await readMultipartFormData(event)
: undefined;
const response = await $fetch.raw(https://*******, {
method,
baseURL: *********,
headers: {
},
body: body
});
return response._data;
}
I'm effectively creating a passthrough API using Nuxt so that the external endpoint isn't exposed to the end user. Just can't figure out how to access the formData in the correct format to pass through on the server side. I don't think I am supposed to use readMultipartFormData() because that seems to be parsing the data somehow whereas I just want to pass the formData straight through to the external API. Any tips?
I've tried using both readMultipartFormData() and readBody() and neither seem to work. I don't actually need to read the body but rather get it and pass it through without any formatting...
If you want to pass the data with formdata to the endpoint try this library:
https://www.npmjs.com/package/object-to-formdata
code:
import { serialize } from 'object-to-formdata';
const formData = serialize(body);
const response = await $fetch.raw(https://*******, {
method,
baseURL: *********,
headers: {
},
body: formData
});
I managed to make it work with ugly solution, first you have to update nuxt to version 3.2.0 min then here my front side
let jobApplicationDTO = {
firstName: values.firstName,
lastName: values.lastName,
email: values.email,
phoneNumber: values.phoneNumber,
company: values.company,
shortDescription: values.shortDescription
};
const formData = new FormData();
formData.append("application", new Blob([JSON.stringify(jobApplicationDTO)], {type: "application/json"}));
formData.append("file", values.file) ;
//formData.append("file", values.file );
await useFetch("/api/application", {
method: "POST",
body: formData,
onResponse({request, response, options}) {
// Process the response data
if (response.status === 200) {
errorMessage.value = "";
successMessage.value = "Your application wa sent successfully, you will be contacted soon !";
}
},
onResponseError({request, response, options}) {
console.debug(response);
if (response.status === 400) {
successMessage.value = "";
errorMessage.value = "There may be an issue with our server. Please try again later, or send an email to support#mantiq.com";
} else {
successMessage.value = "";
errorMessage.value = "Sorry we couldn’t send the message, there may be an issue with our server. Please try again later, or send an email to support#mantiq.com";
}
},
});
}
and server side
import {FormData} from "node-fetch-native";
export default defineEventHandler(async (event) => {
const {BACKEND_REST_API, ENQUIRY_TOKEN} = useRuntimeConfig();
//retrieve frontend post formData
const form = await readMultipartFormData(event);
const applicationUrl = BACKEND_REST_API + '/job/apply'
console.log("url used for enquiry rest call :" + applicationUrl);
console.log("Job application token :" + ENQUIRY_TOKEN);
const formData = new FormData();
console.log(form);
if (form) {
formData.append(form[0].name, new Blob([JSON.stringify(JSON.parse(form[0].data))], {type: form[0].type}));
formData.append(form[1].name, new Blob([form[1].data], {type: form[1].type}), form[1].filename);
}
console.log(formData.values);
return await $fetch(applicationUrl, {
method: "POST",
body: formData,
headers: {
Authorization: ENQUIRY_TOKEN,
},
});
})
What is funny is on frontend you have to create a formData , then to get content and to recreate a formData from your previous formData converted in MultiFormPart[], i created a ticket on nuxt to see how to do it properly

how can I upload images on Zendesk API?

I'm using the function below to upload images on zendesk. Zendesk requires image to be in binary to be uploaded and getting the blob image method is working when I'm uploading on Firebase. These function gets a response 201 but when you check the image, is just a white square. I think image got corrupted during the upload.
imageUri is returned from expo-image-picker like this below:
file:/data/user/0/host.exp.exponent/cache/ExperienceData/.../ImagePicker/8543faa5-b996-46cd-9554-ce9afb61385b.jpg
const uploadImages = async (imageUri) => {
try {
const filename = getFilenameFromImagePicker(imageUri);
const resImg = await fetch(imageUri);
const blobImg = await resImg.blob();
const response = await axios.post(`uploads?filename=${filename}`, {
blobImg
}, {
auth: {
username: membersEmail + '/token',
password: ZENDESK_API_KEY
},
headers: {
'Content-Type': 'application/binary',
}
})
return response.data;
}
catch (error) {
captureException(error);
return null;
}
}
What is the best way to change the image to binary so it can be uploaded to zendesk?
Below is the curl statement from Zendesk's documentation for uploading images
curl "https://{subdomain}.zendesk.com/api/v2/uploads?filename=myfile.dat&token={optional_token}" \
-data-binary #file.dat \
-v -u {email_address}:{password} \
-H "Content-Type: application/binary" \
-X POST
After alot of testing, I was able to make it work. Posting here the answer I come up with incase someone else has the same problem.
async function uploadToServer(sourceUrl) {
// first get our hands on the local file
const localFile = await fetch(sourceUrl);
// then create a blob out of it (only works with RN 0.54 and above)
const fileBlob = await localFile.blob();
// then send this blob to filestack
const serverRes = await fetch('https://www.yourAwesomeServer.com/api/send/file', { // Your POST endpoint
method: 'POST',
headers: {
'Content-Type': application/binary,
},
body: fileBlob, // This is your file object
});
const serverJsonResponse = await serverRes.json();
return serverJsonResponse;
}

Network Error when sending file in react native with axios

I am trying to send a file to a nodejs server from react native using axios, this is my code:
const createFormData = (file) => {
const data = new FormData();
data.append('message', text);
data.append('receiver',doctorid);
if(file !== ''){
data.append('file', {
type: file.type,
uri: file.uri,
name: file.name.replace(/\s/g,'')
})
}
return data;
}
const onSend = async() => {
const newMessages = [...messages]
newMessages.push({"sender": currentuserID, "id": 339, "message": 'sending...', "attachment": '', "receiver": doctorid, "type": 0},)
setMessages(newMessages)
const token = await AsyncStorage.getItem('token');
const data = createFormData(singleFile)
await appApi.post('/chats', data, {
headers: { 'Authorization': 'Bearer ' + token }
}).then(()=>{
socket.emit('sendmessage', text, (err) => {
messageInit()
});
})
.catch(err => console.log(err.message))
}
This code works perfectly if there's no image attached, but ones there's an image attached, I get the network error message immediately.
For a little bit of troubleshooting, I tried sending request to my local machine, using ngrok. From ngrok, I realized the request wasn't sent at all to the url. So it just fails immediately, without the request been made to the url.
Anyone with solution to this.
I'm testing on an android emulator
send using formdata
try this
let formData = new FormData();
let imagefile = document.querySelector('#file');
formData.append("image", imagefile.files[0]);
axios.post('upload_file', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})

Convert Base64 to png and save in the device React Native Expo

I've been trying to save an image on a mobile device with React Native and Expo, i have tried with these packages:
import RNFetchBlob from 'react-native-fetch-blob';
import RNfs from 'react-native-fs ';
but both give me this error when implementing them
null is not an object (evaluating 'RNFetchBlob.DocumentDir')
then try expo-file-system but i don't see any clear example of how to convert base64 and download it to mobile
UPDATE
I was able to do it, my purpose was to save the base64 of a QR and convert it to png and at the same time be able to share it, I did it using expo-file-system and expo-sharing
this is mi code,
import * as FileSystem from 'expo-file-system';
import * as Sharing from 'expo-sharing';
//any image, I use it to initialize it
const image_source = 'https://images.unsplash.com/photo-1508138221679-760a23a2285b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&w=1000&q=80';
share=()=>{
var self = this;
self.setState({loading:true})
FileSystem.downloadAsync(
image_source,
FileSystem.documentDirectory + '.png'
)
.then(({ uri }) => {
console.log(self.state.base64Code);
FileSystem.writeAsStringAsync(
uri,
self.state.base64Code,
{'encoding':FileSystem.EncodingType.Base64}
)
.then(( ) => {
this.setState({loading:false})
Sharing.shareAsync(uri);
})
})
.catch(error => {
console.error(error);
});
}
Actually, I don't know if it's the best way, first write a png image in the directory and then rewrite it with the base64 code, but it worked
This worked for me:
const data = "data:image/png;base64,ASDFASDFASDf........"
const base64Code = data.split("data:image/png;base64,")[1];
const filename = FileSystem.documentDirectory + "some_unique_file_name.png";
await FileSystem.writeAsStringAsync(filename, base64Code, {
encoding: FileSystem.EncodingType.Base64,
});
const mediaResult = await MediaLibrary.saveToLibraryAsync(filename);
Thanks for the update. I've been struggling for days with this on Android and base64 images!
In my case i was trying to upload a base64 image from a signature pad on expo and always got "Network request failed"
I managed to make it work like this hope it helps!
import * as FileSystem from 'expo-file-system';
const uploadBase64 = async (base64String) => {
this.setState({ uploading: true });
//Without this the FilySystem crashes with 'bad base-64'
const base64Data = base64String.replace("data:image/png;base64,","");
try {
//This creates a temp uri file so there's no neeed to download an image_source to get a URI Path
const uri = FileSystem.cacheDirectory + 'signature-image-temp.png'
await FileSystem.writeAsStringAsync(
uri,
base64Data,
{
'encoding': FileSystem.EncodingType.Base64
}
)
//At this point the URI 'file://...' has our base64 image data and now i can upload it with no "Network request failed" or share the URI as you wish
const uploadResult = await this.uploadImageAsync(uri).then(res => res.json())
if (uploadResult) {
this.setState({ image: uploadResult.location });
}
this.setState({ uploading: false });
} catch (e) {
this.setState({ uploading: false });
console.log('*Error*')
console.log(e)
}
}
//Just and ordinary upload fetch function
const uploadImageAsync = (uri) => {
let apiUrl = 'https://file-upload-example-backend-dkhqoilqqn.now.sh/upload';
let formData = new FormData();
formData.append('photo', {
uri,
name: `photo.png`,
type: `image/png`,
});
let options = {
method: 'POST',
body: formData,
headers: {
Accept: 'application/json',
'Content-Type': 'multipart/form-data',
},
};
return fetch(apiUrl, options);
}

nativescript image upload to s3 encoding issue

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