How to upload an image to Cloudflare Images from Node using fetch? - node.js

I'm trying to upload an image from my computer to Cloudflare Images using Node and the fetch API. They provided me with a curl command that works fine.
curl -X POST -F file=#./<file_name> -H "Authorization: Bearer <api_token>" https://api.cloudflare.com/client/v4/accounts/<domain_id>/images/v1
When I tried to convert it to fetch, Cloudflare keeps sending me a 400 in their response.
const cloudflarePostBody = new FormData();
cloudflarePostBody.append("file", fs.createReadStream("testing.jpeg"));
const cloudflareResponse = await fetch("https://api.cloudflare.com/client/v4/accounts/<my_id>/images/v1", {
method: "POST",
headers: {
Authorization: `Bearer ${cloudflareApiToken}`,
"Content-Type": "multipart/form-data"
},
body: cloudflarePostBody,
});
My guess is that I'm doing something wrong with how I'm reading the file with createReadStream, but a lot of the examples I've looked up showed exactly that. Does anyone have any advice? Thanks in advance.

Try fs.readFile() instead of fs.createReadStream():
fs.readFile("testing.jpeg", (err, data) => {
if (err) throw err;
cloudflarePostBody.append("file", new Blob([data]), "testing.jpeg");
});

Related

Convert multipart/form-data cURL to Fetch node js

With the following cURL request I am able to successfully interact with a pulsar api to update the configurations of a function.
Working cURL Request
curl --request PUT -H "Content-Type: multipart/form-data" -F functionConfig='{"parallelism":2};type=application/json' http://pulsar-test:8080/admin/v3/functions/test/ingest/test-entity-FeedTransformer
But I'm having trouble converting to an equivalent fetch request in Node.
Note: I'm using formdata-node to import FormData.
import { FormData } from 'formdata-node'
Non Working Fetch (Bad Request)
const body = new FormData()
body.append('functionConfig', '{"parallelism":2};type=application/json')
return fetch(
'http://pulsar-test:8080/admin/v3/functions/test/ingest/test-entity-FeedTransformer',
{
body,
headers: {
Authorization: 'Bearer abcd'
},
method: 'PUT'
}
)
Apparently the form data is not being set in a way the api server can recognize.
{"reason":"Function config is not provided"}
What am I missing here?
the problem is most likely that multipart request is not recognized/not well formed, or something like that, so try using another library, here's an example with Form-Data:
import { FormData } from 'form-data';
const body = new FormData();
body.append('functionConfig', '{"parallelism":2};type=application/json')
return fetch(
'http://pulsar-test:8080/admin/v3/functions/test/ingest/test-entity-FeedTransformer', {
body,
headers: {
Authorization: 'Bearer abcd'
},
method: 'PUT'
}
)
alternatively, you could run your curl command with child-process
see: How to use curl with exec nodejs

node curl error "Received HTTP code 503 from proxy after CONNECT"

I get a JWT token after authenticating on an API with a POST req.
With that token, I can make a successful curl call from my command prompt:
curl --request GET https://corpURL/customer/wlsAccountManagement/v1/billingAccount/23435657 --header "env: it04" --header "Authorization: Bearer tokenstring..."
But when I try to exec this in node, it fails with Received HTTP code 503 from proxy after CONNECT.
I even tried a basic curl in the command prompt and it works:
curl -v https://corpURL/customer/wlsAccountManagement/v1/billingAccount/23435657
It just tells me I am unauthorized {"message":"Unauthorized"}, which is correct.
In node, I can't even get the {"message":"Unauthorized"}, it still gives me Received HTTP code 503 from proxy after CONNECT.
The reason I'm using curl is because I can see more info. Using axios it gives me a "socket hang up" error.
I have been trying to get this to work and searching online for a solution for over a day. Does anyone have any idea what's going on here? Why does it work in the command prompt but not in node? Thanks!
For anyone who can't get an exec with curl to work in node, or using the packages axios or request, use the https package in node like so:
const options = {
hostname: 'hostDomain', // no https protocol
port: 443,
path: '/path/to/get',
method: 'GET',
headers: {
Authorization: `Bearer tokenString`,
'Content-Type': 'application/json',
},
};
const req = https.request(options, response => {
response.on('data', d => {
process.stdout.write(d);
});
});
req.on('error', e => {
console.error(e);
});
req.end();
This was the only way I was able to get the response so far, in node. I may tweak the structure as it doesn't seem that clean, but it works! You may also need to set the env var for your cert file NODE_EXTRA_CA_CERTS=cert.pem as I already had. Peace.
EDIT
I found a solution with axios, you need to create instances with a baseURL like the https request has it:
const axiosInstance = axios.create({
baseURL: 'https://corpURL',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
env: 'it04',
},
});
axiosInstance
.get('/get/path')
.then(response => console.log('response', response.data))
.catch(err => console.log('err', err));

Get image from Axios and send as Form Data to Wordpress API in a Cloud Function

What I'm trying to accomplish is using a Firebase Cloud Function (Node.js) to:
First download an image from an url (f.eg. from unsplash.com) using an axios.get() request
Secondly take that image and upload it to a Wordpress site using the Wordpress Rest API
The problem seems (to me) to be that the formData doesnt actually append any data, but the axios.get() request actually does indeed retrieve a buffered image it seems. Maybe its something wrong I'm doing with the Node.js library form-data or maybe I get the image in the wrong encoding? This is my best (but unsuccessfull) attempt:
async function uploadMediaToWordpress() {
var FormData = require("form-data");
var formData = new FormData();
var response = await axios.get(
"https://images.unsplash.com/photo-1610303785445-41db41838e3e?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=634&q=80"
{ responseType: "arraybuffer" }
);
formData.append("file", response.data);
try {
var uploadedMedia = await axios.post("https://wordpresssite.com/wp-json/wp/v2/media",
formData, {
headers: {
"Content-Disposition": 'form-data; filename="example.jpeg"',
"Content-Type": "image/jpeg",
Authorization: "Bearer <jwt_token>",
},
});
} catch (error) {
console.log(error);
throw new functions.https.HttpsError("failed-precondition", "WP media upload failed");
}
return uploadedMedia.data;
}
I have previously successfully uploaded an image to Wordpress with Javascript in a browser like this:
async function uploadMediaToWordpress() {
let formData = new FormData();
const response = await fetch("https://images.unsplash.com/photo-1610303785445-41db41838e3e?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=634&q=80");
const blob = await response.blob();
const file = new File([blob], "image.jpeg", { type: blob.type });
formData.append("file", file);
var uploadedMedia = await axios.post("https://wordpresssite.com/wp-json/wp/v2/media",
formData, {
headers: {
"Content-Disposition": 'form-data; filename="example.jpeg"',
"Content-Type": "image/jpeg",
Authorization: "Bearer <jwt_token>",
},
});
return uploadedMedia.data;
},
I have tried the last couple of days to get this to work but cannot for the life of me seem to get it right. Any pointer in the right direction would be greatly appreciated!
The "regular" JavaScript code (used in a browser) works because the image is sent as a file (see the new File in your code), but your Node.js code is not really doing that, e.g. the Content-Type value is wrong which should be multipart/form-data; boundary=----...... Nonetheless, instead of trying (hard) with the arraybuffer response, I suggest you to use stream just as in the axios documentation and form-data documentation.
So in your case, you'd want to:
Set stream as the responseType:
axios.get(
'https://images.unsplash.com/photo-1610303785445-41db41838e3e?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=634&q=80',
{ responseType: 'stream' }
)
Use formData.getHeaders() in the headers of your file upload request (to the /wp/v2/media endpoint):
axios.post( 'https://wordpresssite.com/wp-json/wp/v2/media', formData, {
headers: {
...formData.getHeaders(),
Authorization: 'Bearer ...'
},
} )
And because the remote image from Unsplash.com does not use a static name (e.g. image-name.jpg), then you'll need to set the name when you call formData.append():
formData.append( 'file', response.data, 'your-custom-image-name.jpeg' );
I hope that helps, which worked fine for me (using the node command for Node.js version 14.15.4, the latest release as of writing).

Turning a CURL request into a Node js format with request-promise

I have been trying to convert a working POST CURL request into Node JS format that i can automate. Im reaching a wall with converting it and hoping someone can help.
Here is the curl:
curl -POST https://api-xxxxxxxx/add
-H "Authorization: Bearer xxxxxxx"
-H "Content-Type: multipart/mixed"
-F 'metadata={ "domain_id": 11661, "container_id": 18569512, "name": "TestFile", "size": "1072902", "originalfilename" : "xxx-d6", "mime": "x-html-package",
"attributes":"main_html_file_path=index.html\nduration=10000" }; type=application/json' -F "file=#xxx-d6.x-html-package"
Im using request-promise module to make this request but i might be making a mistake converting it.
let step1Options = {
method: 'POST',
uri: 'https://api-xxx/add',
form: {
metadata: {
domain_id: domainID,
container_id: 1856951,
name: "TestFileNodeJS",
size: "1072902",
originalfilename : "xxx-d6",
mime: "x-html-package",
attributes:"main_html_file_path=index.html\nduration=10000"
},
file: fs.createReadStream("xxx.x-html-package")
},
headers: {
"content-type": "multipart/mixed",
Authorization: "Bearer " + authID
}
};
rp(step1Options)
.then(function (body) {
console.log("success")
console.log(body)
})
.catch(function (err) {
console.log("Error: ", err)
// POST failed...
});
The error im getting:
body:
'{"error":"Unexpected Content-Type header: application/x-www-form-urlencoded. Expecting: multipart/mixed; boundary=<boundary>.","status":"error"}' } }
As far as i know, im changing the Content-Type but still thinks im not. Reading a bit of the documentation of Request-Promise, i dont believe multipart/mixed is a supported? Though i could be wrong.
Does anyone have an idea on how i can get this working or suggest alternatives?

how to do this curl operation in Node.js using request library

How can I convert this curl operation using request Node.js library:
curl -L -X GET -H "Content-Type:application/json" -H "Authorization: authorization..." -H "Scope: 11111111" https://url/download >> file.gz
/*the -L is curl option which means --location Follow redirects (H)
--location-trusted Like '--location', and send auth to other hosts (H)*/
If you just want to download to a file, you can use a head request type.
The request will look like so:
request.head({
url: "https://url/download",
followAllRedirects: true,
headers: {
'Content-Type': 'application/json',
'Authorization':'authorization...',
'Scope': '11111111'
}
}, (err, res, body) => {
request('https://url/download')
.pipe(fs.createWriteStream(`tmp/${res.path}`)).on('close', (data, err) => {
if(err) console.log(`Unable to write to file ${err}`)
console.log('Done')
})
})
I have used a similar snippet which worked well
Use Postmans code generator
Click code on the top left
Paste your curl request
Select Node.js Request from dropdown on top left of popup
You should then get JS snippet converted from your working cURL request
Here is the solution we have to put request inside another because: so the first request returns the url and the second one will download the file
const options = {
url: url,
headers: {
Authorization: `auth`,
'scope': profileId,
'content-type': 'application/json'
},
};
const r = request.get(options, async (err, res, body) => {
const fileStream = fs.createWriteStream(`${path}.gz`);
request(res.request.uri.href).pipe(fileStream);
// path exists unless there was an error
});

Resources