Retrieve then POST a photo to a Foursquare Checkin with Axios - javascript

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.

Related

Upload and pin image with Piniata api on client side, no nodejs

I am trying to use the Piniata api. Here it is:
https://docs.pinata.cloud/
The idea is to upload and pin and image using the api, into my account in Piniata.
I got this sample to upload a file in base64, using Node.js and server side.
The sample use this api call:
"https://api.pinata.cloud/pinning/pinFileToIPFS"
I am suppose to be able to do this in client side as well.
However, there is no sample of client side without using Node.js. And I can't seem to find exactly a documentation of what the api call expects.
Here is the sample I got from the Piniata support:
const { Readable } = require("stream");
const FormData = require("form-data");
const axios = require("axios");
(async () => {
try {
const base64 = "BASE64 FILE STRING";
const imgBuffer = Buffer.from(base64, "base64");
const stream = Readable.from(imgBuffer);
const data = new FormData();
data.append('file', stream, {
filepath: 'FILENAME.png'
})
const res = await axios.post("https://api.pinata.cloud/pinning/pinFileToIPFS", data, {
maxBodyLength: "Infinity",
headers: {
'Content-Type': `multipart/form-data; boundary=${data._boundary}`,
pinata_api_key: pinataApiKey,
pinata_secret_api_key: pinataSecretApiKey
}
});
console.log(res.data);
} catch (error) {
console.log(error);
}
})();
Here is my attempt to perform an upload from client side without Node.js
async function uploadFile(base64Data)
{
const url = `https://api.pinata.cloud/pinning/pinFileToIPFS`;
var status = 0;
try {
let data = new FormData();
var fileName = "FILENAME.png";
var file = new File([base64Data], fileName, {type: "image/png+base64"});
data.append(`data`, file, file.name);
data.append(`maxBodyLength`, "Infinity");
const response = await postData('POST', url, {
'Content-Type': `multipart/form-data; boundary=${data._boundary}`,
"Authorization": "Bearer Redacted"
},
data
);
} catch (error) {
console.log('error');
console.log(error);
}
}
What I get as a response from the server is 400 and the error being:
{"error":"Invalid request format."}
What am I doing wrong?
Also, it seems like when I try to use FormData .append with a stream as a sample, it doesn't work. As if it only expects a blob.

Bad respond when trying to get authentication token for Reddit api 'Application Only OAuth'

So i been trying to get access to the reddit api.
I registered to reddit. verified my mail. opened an app and got my credentials.
Followed this official documentation and also came across to this tutorial
All my efforts have failed and don't get any respond.
I am using nodejs. but also tried in postman and failed.
Tried 2 options using fetch and using axios:
const axios = require('axios');
const fetch = require('node-fetch')
const { URLSearchParams } = require('url')
class RedditApi {
clientId2 = "get ur own credentials by opening an app here https://www.reddit.com/prefs/apps";
clientSecret2 = "get ur own credentials by opening an app here https://www.reddit.com/prefs/apps";
authenticationUrl = `https://www.reddit.com/api/v1/access_token`;
BASE_URL = 'https://www.reddit.com/';
tokenAuth = null;
tokenExpirationTime = null;
currencyObj = null;
constructor(currencyObj) {
this.currencyObj = currencyObj;
console.log("constructor service")
}
async getAuthToken() {
const bodyParams = new URLSearchParams({
grant_type: "https://oauth.reddit.com/grants/installed_client",
device_id: "DO_NOT_TRACK_THIS_DEVICE"
});
console.log(this.clientId2, 'this.clientId');
debugger;
const headersObj = {
'Authorization': `Basic ${Buffer.from(`${this.clientId2}:`).toString('base64')}`,
'Content-Type': 'application/x-www-form-urlencoded',
};
let response = null;
try {
response = await axios.post(this.authenticationUrl,
bodyParams,
{
headers: headersObj
});
debugger;
} catch (error) {
debugger;
console.error(error);
console.log(error.stack);
return null;
}
}
async getAuthToken2() {
try {
// Creating Body for the POST request which are URL encoded
const params = new URLSearchParams()
params.append('grant_type', 'https://www.reddit.com/api/v1/access_token')
params.append('device_id', 'DO_NOT_TRACK_THIS_DEVICE')
// Trigger POST to get the access token
const tokenData = await fetch('https://oauth.reddit.com/grants/installed_client', {
method: 'POST',
body: params,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': `Basic ${Buffer.from(`${this.clientId2}:`).toString('base64')}` // Put password as empty
}
}).then(res => {
debugger;
return res.text()
})
debugger;
if (!tokenData.error) {
debugger;
res.send(trendingResult)
}
res.status(tokenData.error).send(tokenData.message)
} catch (error) {
debugger;
console.log(error)
}
}
}
module.exports = RedditApi;
when using axios i get this respond: "Request failed with status code 401"
When using fetch i get this respond: "'403 Forbidden\nRequest forbidden by administrative rules.\n\n'"
Anybody knows what is the problem and how can i fix it?
Many thanks!

Nest Js file upload not working with javascript FormData

I have some issues parsing a request made from the front-end using FormData. This is the example request generated from Postman for node.js Axios. If I use the postman app with the request, it works as expected.
Frontend example generated from Postman code feature.
var axios = require('axios');
var FormData = require('form-data');
var fs = require('fs');
var data = new FormData();
data.append('file', fs.createReadStream('/some_file.jpg')); **//I am using Electron and I have acces to the FileSystem from the client.**
data.append('resizeLargeImage[width]', '1920');
data.append('resizeLargeImage[height]', '1080');
data.append('resizeLargeImage[type]', 'cover');
var config = {
method: 'post',
url: 'localhost:3030/api/v1/optimize-single',
headers: {
'x-api-key': '123',
...data.getHeaders()
},
data : data
};
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});
Backend
#Post('/optimize-single')
#UseInterceptors(FileInterceptor('file'))
async uploadFile(
#UploadedFile() file: FileDto,
#Body() body: UploadFileParametersDto,
#Res() response: Response,
): Promise<any> {
console.log('file', file, 'body', body);
**//File is undefined, body is a null Object**
return await this.appService.uploadFile(file, body, response);
}
Any ideas as to why Nest doesn't recognize this type of request?
Thanks!
I managed to figure it out.
If you are in the Renderer process, it works by attaching the file as a blob.
const fileBuffer = fs.readFileSync(filePath);
const fileName = path.basename(filePath);
const blob = new Blob([fileBuffer], {
type: mime.lookup(filePath),
});
formData.append('file', blob, fileName);
If you move the same functionality in the Main process it will work as expected with data.append('file', fs.createReadStream('/some_file.jpg'));

Understanding HTTP requests --- authenticating with rapidgator--- how does it work?

I am confused about http requests and how it works when requesting links.
So I know a GET requests adds parameters to urls like ?username=ABC123&password=TTT and POST just sends data to the server but thats all I get when reading tutorials out there. How would I for example send a request and download a file from a rapidgator link after authenticating? (I am a premium user).
Link to their api doc:
https://support.rapidgator.net/index.php?/Knowledgebase/Article/View/89/9/do-you-have-api
The first response gives back a response object like this which gives the session id which is where I got the session id from:
{
"response": {
"expire_date":1351526400,
"traffic_left":0,
"reset_in":11695
},
"response_status":200,
"response_details":null
}
This is my code:
const rp = require('request-promise');
const url = 'http://rapidgator.net/api/user/login';
const opts = {
uri: url,
qs: {
username: '**censored**',
password: '**censored**',
},
headers: {
'User-Agent': 'Request-Promise'
},
json: true
}
rp(opts)
.then(( resp ) => {
const sessionId = resp.session_id;
const postOpts = {
uri: 'http://rapidgator.net/api/user/info?sid=knf3pqpg3ldm05qnol0sn16326',
method: 'POST',
body: {
session_id: sessionId
},
json: true
}
rp(postOpts)
.then(res => {
console.log(cyan(JSON.stringify(res, null, 2)));
})
})
.catch(( err ) => {
throw Error(err);
});
Am I approaching this the wrong way? How do I proceed to download links from rapidgator?

Uploading to Cloudinary API - Invalid file parameter

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'
}
});

Categories