Corrupt zip when uploading via Axios POST - javascript

I'm 'publishing' a zip file to a server app via a REST endpoint. If I POST via Postman or the app's frontend, I get a published zip file which is valid. I can turn around download it, and open it, etc.
If I attempt to do the same thing with my code and Axios, the server app's attempt to unzip and use the content I've uploaded fails. If I DL the archive, it is corrupt. The fact that the same archive works via Postman & the app's UE tells me this is either a PEBKAC or potentially an issue with Axios itself. Here is the code I'm using to POST to the endpoint. Note that at the end, I'm actually writing the data I POST to a local file on my machine as a zip so I can verify I'm not doing anything dumb whenI read the file via fs. The local copy of the file I created is fine and dandy.
Note how I'm actually hard-coding content-length, as well. I'm testing with a single file and I've verified the length is correct via fs.statSync AND that it matches the Content-Length I see when I upload via Postman & the App's UE.
var uploadFile = (data, fileInfo) => new Promise(resolveUpload => {
console.log("Starting Upload API call for:", fileInfo.description);
axios.post(aepServer + '/api/v1/files',
data, {
jar: cookieJar,
withCredentials: true,
headers: {
'Content-Type': 'application/octet-stream',
'path': fileInfo.path,
'description': fileInfo.description,
'Content-Length': 354198,
'Accept-Encoding': 'gzip, deflate, br',
'Accept': '*/*',
'Connection': 'keep-alive'
},
// DANGER: allow self-signed certs workaround which I must remove
httpsAgent: new https.Agent({
rejectUnauthorized: false,
})
}).then((response) => {
fileResponse = response.data;
console.log('\n', chalk.bgMagenta('FILE UPLOADED: '), response);
fs.writeFileSync('c:\\data\\newfile.zip', data, 'binary');
resolveUpload(fileResponse);
})
.catch((err) => {
console.log("AXIOS ERROR: ", err);
})
});
Does anything look wrong here? While looking at the response object, I do see something that has me scratching my head:
headers: {
Accept: '*/*',
'Content-Type': 'application/octet-stream',
path: '/Workspaces/Public%20Apps/UFOs.yxzp',
description: 'UFO Sitings in the US, 1995 to present',
'Content-Length': 532362,
'Accept-Encoding': 'gzip, deflate, br',
Connection: 'keep-alive',
Cookie: 'ayxSession=s%3Ac39f55a3-b219-43a5-9f8a-785e1222c81c.QR4KI8uXaQlL9axqkO8AkyabPVt3i37nGbz%2FJef0eqU',
'User-Agent': 'axios/0.19.2'
},
Look at the content length: 532362 bytes. It seems like the ~354k value I hard-coded in the headers is being ignored somehow. Might this be my problem? [BTW, if I use the same code to upload a csv or txt file, all is well - this seems related to compressed files only]
EDIT: Welp, it looks like Axios does override that property and there's nothing I can do about it: Axios set Content-Length manually, nodeJS. Now the question is if setting this incorrectly would munge the file and WHY the value is wrong. When I do a data.length, I get the 354198 value.

can you try with 'multipart/form-data'
axios.post('upload_file', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})
alternatively get the CURL request using postman and use the header information supplied by postman

My answer was here:
Reading binary data in node.js
In essence, I was reading the zip file as 'binary" when I shouldn't have passed anything in at all.
var content = fs.readFileSync(aFile.readFrom);
//NOT
//var content = fs.readFileSync(aFile.readFrom, 'binary');

Related

YouTubeAPI: How to upload thumbnail (JS)

I tried uploading thumbnail on youtube using this guide: https://developers.google.com/youtube/v3/docs/thumbnails/set
I was able to successfully run it on postman using this curl:
curl --location --request POST 'https://www.googleapis.com/upload/youtube/v3/thumbnails/set?videoId=<video id>' \
--header 'Authorization: OAuth <token>' \
--header 'Content-Type: image/png' \
--form 'thumbnail=#"/C:/Users/user/Pictures/nami.PNG"'
However I have trouble translating that into js, what I did so far is:
// the "file" is the File from <input type="file"> - data on this looks ok
uploadThumbnail async (file) {
const formData = new FromData();
const formData.append('thumbnail', file, 'test.png');
await fetch.post('https://www.googleapis.com/youtube/v3/thumbnails/set', {
headers: {
Authorization: 'Oauth <token>',
'Content-Type': 'multipart/form-data' // I also tried using the file.type here (image/png)
},
query: {
videoId: <video id>
},
body: formData,
})
}
(to simplify the logic, I only manually typed the code above, so pardon if there are any typo.)
but this throws The request does not include the image content. I don't understand, I also tried converting the File into Blob, but same error.
As pointed out on the comments on my main post, I combined the answers and came up with this (this works!)
await fetch.post(`https://www.googleapis.com/upload/youtube/v3/thumbnails/set?videoId=${videoId}&uploadType=media`, {
headers: {
Authorization: 'Bearer <token>',
'Content-Type': file.type
},
body: file,
})
Mistakes are:
My endpoint is wrong and is missing uploads (this API is different from other youtube endpoints, so if you are reusing a variable base_url better check it out.
Using Oauth instead of Bearer.
There are no query in fetch
No need to convert and add the formData, pass the file directly instead.

Calling a API via fetch() - Unexpected end of input

I want to change an API call (external source, no chance to change something on the API side) from PHP to Javascript (Learning purposes).
Because of the cross-origin, I use fetch(). When I run my script, I get an Unexpected end of input error and can't figure out why.
function postData(url = '', data = {}) {
var headers = new Headers();
headers.set('Authorization', 'Basic ' + window.btoa("user" + ':' + "pass"));
return fetch(url, {
method: 'POST',
mode: 'no-cors',
cache: 'no-cache',
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
redirect: 'follow',
referrer: 'no-referrer',
body: JSON.stringify(data),
}).then(response => response.json()).catch(error => console.error(error));
}
postData('https://www.api-endpoint.com/cat1/api/search?', {
"searchID": "710",
"isTagged": true
}).then(data => console.log(JSON.stringify(data))).catch(error => console.error(error));
How can I identify the problem with this code? It seems the Authorization is okay. I implemented the search parameters (searchID and isTagged) as described on the manual from the API Dev.
Thanks in advance!
You said mode: 'no-cors', which disables everything which requires CORS permission.
Since reading data across origins requires CORS permission, there is no data.
Trying to parse an empty string as JSON results in the unexpected end of input because the input ended before there was any JSON.
(Note that other things which require CORS permissions, and which you are trying to do, include setting the content-type to JSON and including credentials).

NodeJS HTTPOnly cookie not being sent with fetch function

I'm having problems with cookie authentication between an expressJS server and a VueJS font-end.
When logging in through the site, I successfully get a HTTPOnly Cookie in the set-cookie header:
Screenshot (Ignore the Auth header, using it for testing only)
I also see the cookie in the devTools, and everything looks right too me, I'm not an expert on cookies though so it may not be correct
The problem is when I request the user's settings on another endpoint, the cookie is not sent to the server. The req.cookie object is empty when the this request is handled on the server side.
Here is my fetch code:
const loginOptions = {
method: 'POST',
mode: 'cors',
cache: 'no-cache',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: email,
password: password,
}),
credentials: 'same-origin',
};
const settingsOptions = {
method: 'GET',
mode: 'cors',
cache: 'no-cache',
headers: {
'Content-Type': 'application/json',
},
credentials: 'same-origin',
};
const loginResponse = await fetch(baseUrl + '/login', loginOptions);
const userSettings = await fetch(baseUrl + '/settings', settingsOptions);
I've tried using credentials: "include", without success.
On the express server I'm using cors like this:
app.use(cors({
origin: '*',
credentials: true,
}));
Here is also an example of the second request, the 403 status is set by the server when no cookie is attached to the request.
I've tried setting the domain of the cookie to both localhost and 127.0.0.1 as suggested in another thread. I have left it on localhost for now.
Solved
I had read somewhere that you should add a specific domain value to the cookie when creating it. If I just removed that setting, it sets it automatically I'm guessing, and then it worked! So my guess is that I had set the domain value to the wrong value for what I was trying to do
Your response has access-control-allow-origin: http://localhost:8080 which implies you are making a cross-origin request.
You said:
credentials: 'same-origin',
… which tells your client-side code to only include credentials for same-origin requests.
I read somewhere that Chrome wasn't friendly with cookies and localhost env, maybe it could be that.
https://bugs.chromium.org/p/chromium/issues/detail?id=56211
Furthermore, I had some problems with cookies, express and vueJS some times ago.
Maybe it can help you: SetCookie header not stored
I had read somewhere that you should add a specific domain value to the cookie when creating it. If I just removed that setting, it sets it automatically I'm guessing, and then it worked! So my guess is that I had set the domain value to the wrong value for what I was trying to do

react + redux - 401 - unauthorized - missing headers in Request Headers

return fetch(`{SERVICE API URL}`, {
method: 'GET',
headers: {
'userName': "username",
'password': "password",
'content-type': 'application/json'
}
})
.then(response => response.json())
.then(json => dispatch(receivePosts(reddit, json)))
I'm trying to get service API data with authorization headers, but getting 401 - Unauthorized error and the response is Missing Request Headers.
Tried with sending authorization content with body also - getting same error 401 - Unauthorized error.
Edited:
headers: {
'userName': "xyz",
'sessionToken': "xyz................."
}
When I'm checking with Postman client it is working fine, but not with the redux-saga fetch method. Kindly help me for this.
Looks like it's a backend problem - CORS Filter configuration
If the backend is on a different server (could be on the same machine, but in a different Application Server, in other words, on a different port) you have to do some CORS Filters configurations.
The frontend code is running on a server - that means it's an application. Postman is a client, just like Google Chrome or any other browser. That's the explanation why you can do the request without any problem from Postman but unsuccessful from your frontend application.
I guess you enabled the Access-Control-Allow-Origin header on the backend
Now you have to allow your custom headers with Access-Control-Allow-Headers
Whenever I use fetch and I need to add headers to the request I do it this way:
headers: new Headers({
Accept: 'application/json',
Authorization: token,
'Content-Type': 'application/json',
}),
so you might want to try this approach, also in order to debug this issue you might want to check your Netowrk tab and verify which headers are sent with the request.
You need to add an Authorization bearer header.
For instance:
headers = new Headers({
'Authorization': `Bearer ${authorizationCodeOrCredentials}`
});
In your code:
return fetch(`{SERVICE API URL}`, {
method: 'GET',
headers: {
'Authorization': 'Bearer ' + someValue, // Add this line
'userName': "username",
'password': "password",
'content-type': 'application/json'
}
})
.then(response => response.json())
.then(json => dispatch(receivePosts(reddit, json)))
If you are using Linux system & If you have chrome in it...
Run your chrome using following command
/opt/google/chrome/chrome --disable-web-security --user-data-dir
Try now, If everything works fine then it's CORS issue from Backend.

node 'fs' and 'request' to upload file via REST API

I am trying to use a REST API to upload a file. I have a function that works for every other type of request, but this one (which uploads a file) doesn't want to work:
var createItem = function ()
{
request.post(
{
url:browser.params.baseRestUrl + 'repositories/Samples/items',
auth: browser.params.auth,
body: fs.createReadStream(filepath),
form:
{
headers: {'content-type': 'application/octet-stream',
'Content-Disposition': 'attachment; filename="oneHourSally3.fmw"',
'Accept': 'application/json'
},
}
},
function(e,r,user){ console.log("Status code of createItem('oneHourSally'): " + r.statusCode);});
};
This returns a http 415 (unsupported media type) error.
Usually that error implies that the server is only willing to accept a specific Content-Type. You're specifying application/octet-stream and the server tells you it doesn't accept that. Consult the documentation of the API to figure out what is supported, or contact the authors of the API via a support channel.

Categories

Resources