I am working on integrating Cloudinary with my React Native app and am running into a problem when I go to upload using the Cloudinary API. I'm using React Native Image Picker to select an image from the camera roll and using that I get a source uri - example below.
I am getting an error response back from Cloudinary and I'm not sure what it's referring to. "Invalid file parameter. Make sure your file parameter does not include '[]'"
When I use the debugger, I can console log out all the params I am sending in the body of my request. Any suggestions would be much appreciated!
source.uri: /Users/IRL/Library/Developer/CoreSimulator/Devices/817C678B-7028-4C1C-95FF-E6445FDB2474/data/Containers/Data/Application/BF57AD7E-CA2A-460F-8BBD-2DA6846F5136/Documents/A2F21A21-D08C-4D60-B005-67E65A966E62.jpg
async postToCloudinary(source) {
let timestamp = (Date.now() / 1000 | 0).toString();
let api_key = ENV.cloudinary.api;
let api_secret = ENV.cloudinary.api_secret
let cloud = ENV.cloudinary.cloud_name;
let hash_string = 'timestamp=' + timestamp + api_secret
let signature = CryptoJS.SHA1(hash_string).toString();
let upload_url = 'https://api.cloudinary.com/v1_1/' + cloud + '/image/upload'
try {
let response = await fetch(upload_url, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
file: {
uri: source.uri,
type: 'image/jpeg'
},
api_key: api_key,
timestamp: timestamp,
signature: signature
})
});
let res = await response.json();
console.log(res);
} catch(error) {
console.log("Error: ", error);
}
}
UPDATE
So I now have base64 encoding working, I think, but I am still getting the same error.
var wordArray = CryptoJS.enc.Utf8.parse(source.uri);
var file = CryptoJS.enc.Base64.stringify(wordArray);
try {
let response = await fetch(upload_url, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
file: {
uri: file,
type: 'image/jpeg;base64'
},
api_key: api_key,
timestamp: timestamp,
signature: signature
})
});
So it turns out the source data I was passing in was not formatted correctly. I was able to pass it in from the ImagePicker plugin I was using as an already formatted data URI (the ImagePicker example comes with two ways to capture your source file and I was using the wrong one). I was able to get rid of the CryptoJS stuff and simply pass in file: source.uri
If you are using axios, make sure to include {'X-Requested-With': 'XMLHttpRequest'} in your request headers. eg.
const uploadAgent = axios.create({
baseURL: 'https://api.cloudinary.com',
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
});
Related
I have a sting variable of a csv. I want to upload it to a slack channel as a .csv file, not as text.
async function run() {
const csvData = 'foo,bar,baz';
const url = 'https://slack.com/api/files.upload';
const res = await axios.post(url, {
channel: '#csvchannel',
filename: 'CSVTest.csv',
content: csvData
}, { headers: { authorization: `Bearer ${slackToken}` } });
console.log('Done', res.data);
}
This code returns: error: 'no_file_data', Changing content to file gives the same response.
What do I have to do to convert the csv sting into a file that can be uploaded? I can't use fs to write out the file.
I have tried to use fs.createReadStream(csvData) but that needs a file, not a string.
Slack API documentation: https://api.slack.com/methods/files.upload
You don't need to convert the CSV into a file, seems you are missing a couple of things here:
fileType property, it needs to be CSV.
Slack file upload API supports multipart/form-data and
application/x-www-form-urlencoded content types.
You're missing the Content-Type.
Check out a working example of how you could send the data using application/x-www-form-urlencoded
Send a CSV to Slack
View in Fusebit
const csvData = 'foo,bar,baz';
const url = 'https://slack.com/api/files.upload';
const params = new URLSearchParams()
params.append('channels', slackUserId);
params.append('content', csvData);
params.append('title', 'CSVTest');
params.append('filetype', 'csv');
const result = await axios.post(url, params,
{
headers:
{
authorization: `Bearer ${access_token}`,
'Content-Type': 'application/x-www-form-urlencoded'
}
});
ctx.body = { message: `Successfully sent a CSV file to Slack user ${slackUserId}!` };
I want to upload an image file to the server in react native, how can i do so?
Here is my code :
In my index.js Which is my entry point I have set axios default configurations :
axios.defaults.baseURL = BaseUrl;
axios.defaults.headers.common['Content-Type'] = 'application/json';
axios.defaults.headers.common['Accept'] = 'application/json';
Now in my profileEdit.js, where I need to upload a profile image
let data = new FormData();
data.append('profile_picture',
{uri: imageResponse.uri, name: imageResponse.fileName, type: 'image/jpg'});
// imageResponse is a response object that I m getting from React native ImagePicker
axios.post('profile/picture/', data,
{
headers: {
"Authorization": "JWT " + this.state.token,
'content-type': 'multipart/form-data',
}
}).then(response => {
console.log('Profile picture uploaded successfully', response);
}).catch(error => {
console.log('Failed to upload profile image', error);
console.log('Error response',error.response);
});
But this giving me network error, while other APIs are working fine.
I followed the solution given here How to upload image to server using axios in react native?
This is the error response I am getting.
And my request header is like this
I don't want to use any other package such as react native fetch blob
More links that I followed :
axios post request to send form data
Can anyone please tell me where I m doing wrong or how shall I approach to this problem. ty
I think your header for the Axios POST request may be incorrect since you're using a FormData() constructor to store your image data:
You may want to try the following instead:
let data = new FormData();
data.append('profile_picture',{
uri: imageResponse.uri,
name: imageResponse.fileName,
type: 'image/jpg'}
);
axios.post('profile/picture/', data,
{
headers: {
'content-type': `multipart/form-data; boundary=${data._boundary}`,
...data.getHeaders()
}
}
)
For me simply work
const oFormData = new FormData();
oFormData.append("image", {
uri: images.uri,
type: images.type,
name: images.fileName
});
axios.post(servicesUrl.imageupload, oFormData);
I'm trying to upload a file with the Google Drive api, and I have the metadata correct, and I want to ensure that the actual file contents make it there. I have a simple page setup that looks like this:
<div id="upload">
<h6>File Upload Operations</h6>
<input type="file" placeholder='file' name='fileToUpload'>
<button id='uploadFile'>Upload File</button>
</div>
and I have a the javascript setup where the user is prompted to sign in first, and then they can upload a file. Here's the code: (currently only uploads the file metadata....)
let uploadButton = document.getElementById('uploadFile');
uploadButton.onclick = uploadFile;
const uploadFile = () => {
let ftu = document.getElementsByName('fileToUpload')[0].files[0];
console.dir(ftu);
gapi.client.drive.files.create({
'content-type': 'application/json;charset=utf-8',
uploadType: 'multipart',
name: ftu.name,
mimeType: ftu.type,
fields: 'id, name, kind'
}).then(response => {
console.dir(response);
console.log(`File: ${ftu.name} with MimeType of: ${ftu.type}`);
//Need code to upload the file contents......
});
};
First, I'm more familiar with the back end, so getting the file in bits from the <input type='file'> tag is a bit nebulous for me. On the bright side, the metadata is there. How can I get the file contents up to the api?
So According to some resources I've found in my three day search to get this going, the file simply cannot be uploaded via the gapi client. It must be uploaded through a true REST HTTP call. So let's use fetch!
const uploadFile = () => {
//initialize file data from the dom
let ftu = document.getElementsByName('fileToUpload')[0].files[0];
let file = new Blob([ftu]);
//this is to ensure the file is in a format that can be understood by the API
gapi.client.drive.files.create({
'content-type': 'application/json',
uploadType: 'multipart',
name: ftu.name,
mimeType: ftu.type,
fields: 'id, name, kind, size'
}).then(apiResponse => {
fetch(`https://www.googleapis.com/upload/drive/v3/files/${response.result.id}`, {
method: 'PATCH',
headers: new Headers({
'Authorization': `Bearer ${gapi.client.getToken().access_token}`,
'Content-Type': ftu.type
}),
body: file
}).then(res => console.log(res));
}
The Authorization Header is assigned from calling the gapi.client.getToken().access_token function, and basically this takes the empty object from the response on the gapi call and calls the fetch api to upload the actual bits of the file!
In your situation, when you upload a file using gapi.client.drive.files.create(), the empty file which has the uploaded metadata is created. If my understanding is correct, how about this workaround? I have experienced the same situation with you. At that time, I used this workaround.
Modification points:
Retrieve access token using gapi.
File is uploaded using XMLHttpRequest.
Modified script:
Please modify the script in uploadFile().
let ftu = document.getElementsByName('fileToUpload')[0].files[0];
var metadata = {
'name': ftu.name,
'mimeType': ftu.type,
};
var accessToken = gapi.auth.getToken().access_token; // Here gapi is used for retrieving the access token.
var form = new FormData();
form.append('metadata', new Blob([JSON.stringify(metadata)], {type: 'application/json'}));
form.append('file', ftu);
var xhr = new XMLHttpRequest();
xhr.open('post', 'https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&fields=id,name,kind');
xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken);
xhr.responseType = 'json';
xhr.onload = () => {
console.log(xhr.response);
};
xhr.send(form);
Note:
In this modified script, it supposes that Drive API is enabled at API console and the access token can be used for uploading file.
About fields, you are using id,name,kind. So this sample also uses them.
Reference:
gapi
If I misunderstand your question or this workaround was not useful for your situation, I'm sorry.
Edit:
When you want to use fetch, how about this sample script?
let ftu = document.getElementsByName('fileToUpload')[0].files[0];
var metadata = {
'name': ftu.name,
'mimeType': ftu.type,
};
var accessToken = gapi.auth.getToken().access_token; // Here gapi is used for retrieving the access token.
var form = new FormData();
form.append('metadata', new Blob([JSON.stringify(metadata)], {type: 'application/json'}));
form.append('file', ftu);
fetch('https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&fields=id,name,kind', {
method: 'POST',
headers: new Headers({'Authorization': 'Bearer ' + accessToken}),
body: form
}).then((res) => {
return res.json();
}).then(function(val) {
console.log(val);
});
With https://www.npmjs.com/package/#types/gapi.client.drive
const makeUploadUrl = (fileId: string, params: Record<string, boolean>) => {
const uploadUrl = new URL(
`https://www.googleapis.com/upload/drive/v3/files/${fileId}`
)
Object.entries({
...params,
uploadType: 'media',
}).map(([key, value]) => uploadUrl.searchParams.append(key, `${value}`))
return uploadUrl
}
const uploadDriveFile = async ({ file }: { file: File }) => {
const params = {
enforceSingleParent: true,
supportsAllDrives: true,
}
// create file handle
const { result } = await gapi.client.drive.files.create(params, {
// CAN'T have the upload type here!
name: file.name,
mimeType: file.type,
// any resource params you need...
driveId: process.env.DRIVE_ID,
parents: [process.env.FOLDER_ID],
})
// post the file data
await fetch(makeUploadUrl(result.id!, params), {
method: 'PATCH',
headers: new Headers({
Authorization: `Bearer ${gapi.client.getToken().access_token}`,
'Content-Type': file.type,
}),
body: file,
})
return result
})
}
I have used https://graph.microsoft.com/beta/me/photo/$value API to get the profile picture of the outlook user. I get an image on running the above API in the rest-client. The content-type of the API is "image/jpg"
But, in Node.js, the response of the API is as follows:
����\u0000\u0010JFIF\u0000\u0001\u0001\u0000\u0000\u0001\u0000\u0001\u0000\u0000��\u0000�\u0000\u0005\u0005\u0005\u0005\u0005\u0005\u0006\u0006\u0006\u0006\b\t\b\t\b\f\u000b\n\n\u000b\f\u0012\r\u000e\r\u000e\r\u0012\u001b\u0011\u0014\u0011\u0011\u0014\u0011\u001b\u0018\u001d\u0018\u0016\u0018\u001d\u0018+"\u001e\u001e"+2*(*2<66<LHLdd�\u
I used 'fs' to create an image file. The code is as follows:
const options = {
url: "https://graph.microsoft.com/beta/me/photo/$value",
method: 'GET',
headers: {
'Accept': 'application/json',
'Authorization': `Bearer ${locals.access_token}`,
'Content-type': 'image/jpg',
}
};
request(options, (err, res, body) => {
if(err){
reject(err);
}
console.log(res);
const fs = require('fs');
const data = new Buffer(body).toString("base64");
// const data = new Buffer(body);
fs.writeFileSync('profile.jpg', data, (err) => {
if (err) {
console.log("There was an error writing the image")
}
else {
console.log("The file is written successfully");
}
});
});
The file is written successfully, but the .jpg image file generated is broken. I am unable to open the image.
The output of the image file is as follows:
77+977+977+977+9ABBKRklGAAEBAAABAAEAAO+/ve
You can do this by streaming the response like this,
request(options,(err,res,body)=>{
console.log('Done!');
}).pipe(fs.createWriteStream('./profile.jpg'));
https://www.npmjs.com/package/request#streaming
https://nodejs.org/api/fs.html#fs_class_fs_writestream
The reason for this is that by default, request will call .toString() on the response data. In case of binary data, like a RAW JPEG, this isn't what you want.
It's also explained in the request documentation (albeit vaguely):
(Note: if you expect binary data, you should set encoding: null.)
Which means that you can use this as well:
const options = {
encoding : null,
url : "https://graph.microsoft.com/beta/me/photo/$value",
method : 'GET',
headers : {
'Accept' : 'application/json',
'Authorization' : `Bearer ${locals.access_token}`,
'Content-type' : 'image/jpg',
}
};
However, streaming is probably still the better solution, because it won't require the entire response being read into memory first.
Request is deprecated. You can do this with axios;
// GET request for remote image in node.js
axios({
method: 'get',
url: 'http://example.com/file.jpg',
responseType: 'stream'
})
.then(function (response) {
response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))
});
I'm trying to retrieve, then POST a JPEG image to Foursquare's https://api.foursquare.com/v2/photos/add endpoint using Axios in Node. I've tried a few methods with Axios (and Postman) but always receive the same error response of Missing file upload:
{
"meta": {
"code": 400,
"errorType": "other",
"errorDetail": "Missing file upload",
"requestId": "NNNNNNNNNNNNNNNNNNNNN" // not the true requestId
},
"notifications": [
{
"type": "notificationTray",
"item": {
"unreadCount": 0
}
}
],
"response": {}
}
The image is created using the Google Static Map API and retrieved with an Axios GET request:
const image = await axios.get(imageURL, {
responseType: "arraybuffer"
});
which is wrapped in an async function and successfully returns a buffer. The data is read into a Buffer and converted to a string:
const imageData = new Buffer(image, "binary").toString();
Here's an example imageData string. I've also tried converting the string to base64.
This string is then POSTed to the Foursquare endpoint:
const postPhoto = await axios.post(
"https://developer.foursquare.com/docs/api/photos/add?
checkinId=1234&
oauth_token=[TOKEN]&
v=YYYYMMDD",
imageData,
{
headers: { "Content-Type": "image/jpeg" }
}
);
where the checkinId, oauth_token and v params are all valid.
I've tried different Content-Type values, base64 encoding the imageData and several other solutions found in forums and here on SO (most are several years old), but nothing works. The response errorDetail always says Missing file upload.
The issue could be in how the POST request is structured, but I could also be requesting/handling the image data incorrectly. A 2nd (or 3rd, or 4th) set of eyes to check I'm putting this together would be super helpful.
Whew, I have finally solved this.
I was eventually able to get it working thru Postman which provided some hints. Here's the Postman code snippet using request:
var fs = require("fs");
var request = require("request");
var options = { method: 'POST',
url: 'https://api.foursquare.com/v2/photos/add',
qs:
{ checkinId: [MY CHECKING ID],
public: '1',
oauth_token: [MY OAUTH TOKEN],
v: [MY VERSION] },
headers:
{ 'postman-token': '8ce14473-b457-7f1a-eae2-ba384e99b983',
'cache-control': 'no-cache',
'content-type': 'multipart/form-data; boundary=---- WebKitFormBoundary7MA4YWxkTrZu0gW' },
formData:
{ file:
{ value: 'fs.createReadStream("testimage.jpg")',
options: {
filename: 'testimage.jpg',
contentType: null
}
}
}
};
request(options, function (error, response, body) {
if (error) throw new Error(error);
console.log(body);
});
The key part of this was fs.createReadStream(). The part I was missing before was to pass the image as a stream to the request.
Using this I was able to figure out the Axios request:
const axios = require("axios");
const querystring = require("qs");
const FormData = require("form-data");
const getImageStream = async function(url) {
return await axios
.get(url, {
responseType: "stream"
})
.then(response => response.data);
};
let form = new FormData();
form.append("file", getImageStream([IMAGE URL]));
const requestURL = "https://api.foursquare.com/v2/photos/add";
const requestParams = {
checkinId: [MY CHECKIN ID],
public: 1,
oauth_token: [MY OAUTH TOKEN],
v: [MY VERSION]
};
const requestConfig = {
headers: form.getHeaders()
};
try {
const postPhoto = await axios.post(
requestURL + "?" + querystring.stringify(requestParams),
form,
requestConfig
);
return postPhoto;
} catch (e) {
console.error(e.response);
}
And voila, the request succeeds and the image is posted to the Foursquare checkin.