Download image using axios - node.js

I want to download image uploaded on different origin. My frontend is in react and running on https://example.com and images are uploading to https://backend.example.com and backend is in express/node. I am using axios to download the image But axios is returning CORS error.
Here is my script to download
const resp = await axios.get(url, {
responseType: "blob",
})
.then((resp) => resp.data);
const downloadUrl = URL.createObjectURL(
new Blob([resp])
);
const link = document.createElement("a");
link.href = downloadUrl;
link.setAttribute("download", name);
document.body.appendChild(link);
link.click();
link.remove();
document.body.removeChild(link);
URL.revokeObjectURL(downloadUrl);
I have used cors library to handle cors on server side
app.use(cors({origin: "*", credentials: true}));
this is working for other requests but not for images.
I have deployed client and server on cpanel. While running on localhost download is working as expacted but on cpanel server I am facing this issue.
Any help will be appreciated Thanks

Related

I can't render images from an API in my fronted application

I'm building an appplication using react at the front and express at the back in order to avoid cors issues. From the back I get the Json of my API from "Zoho Creator". Here an example.
As you can see I get a Json object, but in the image value, the URL appear without https://zoho.creator.eu... and when I try to request them from my frontend I can't render them. and if I add https://... at the beginning manually I get an error 401. I also tried to add the https route from the backend but is the same thing.
Here my backend using express
PD: I'm a rookie I know, please don't judge me.
const express = require("express")
const app = express()
const fetch = require("node-fetch")
const bodyParser = require("body-parser");
const cors = require("cors")
const PORT = process.env.PORT || 4000;
app.use(bodyParser.json());
const urlPost = ("https://accounts.zoho.eu/oauth/v2/token?refresh_token=1000.3dbdad6937dc0800c4dcc662cd14d173.86efb18e337989bebb3ff4c05582c94c&client_id=1000.NQL17JHK3Y62Y178TO0E3FQC6MBQJV&client_secret=5d04ad135862e7313377484af55efa1f41c1f49a39&grant_type=refresh_token")
const urlGet = "https://creator.zoho.eu/api/v2/hostienda1/Product-Catalog/report/Product_Details";
app.use(cors())
app.get
const peticion = fetch(urlPost,{
method: 'POST',
redirect: 'follow'
});
peticion
.then((ans)=>{return ans.json()})
.then((resp)=>{
const reslt = resp.access_token;
return app.get("*", async (req,res)=>{
const response = await fetch(urlGet,{
method: "GET",
headers:{
'Authorization':`Zoho-oauthtoken ${reslt}`,
}})
const result = await response.json()
const test = result.data
test.map(function(product){
if (true){
product.Product_Images[0] = "https://creator.zoho.eu" + product.Product_Images[0].display_value
return product.Product_Images[0]
}
})
res.json(test)
})
})
app.listen(PORT, () => {console.log(`Listening on port ${PORT}`)})`
I hope to render my images from my frontend app.
I assume that the download link for the image also requires authentication, in other words, the download requires a request like
GET https://creator.zoho.eu/api/v2/hostienda1/Product-Catalog/report/Product_Details/...jpg
Authorization: Zoho-oauthtoken ...
Such a request cannot be made your frontend, because it does not know the Zoho-oauthtoken.
This means that you must make this request in your backend. Rewrite your middleware so that it retrieves the download link for one image only (currently you return test, which contains many images). Then use the following code to access the image at that download link and return it to the frontend:
var img = await fetch("https://creator.zoho.eu/api/v2/...", // the download link
{headers: {authorization: `Zoho-oauthtoken ${reslt}`}}
);
res.set("content-type", img.headers.get("content-type"));
stream.Readable.fromWeb(img.body).pipe(res);

axios get request to express server is only successful for the first time

I'm trying to setup a route for downlading videos for my Vue app backed by an Express server. For some reason, first request that is sent to backend is working as expected and it results in successful file download; however, the subsequent requests fail with Network Error, and I only get a brief error message that looks like this http://localhost:8080/download/videos/1667163624289.mp4 net::ERR_FAILED 200 (OK).
What could be the issue here?
I have an Express.js server (localhost:8000) setup with cors like below:
const express = require("express");
const app = express();
const port = 8000;
const cors = require("cors");
app.use(cors());
app.get("/download/:kind/:fileName",
async (req, res, next) => {
const file = `${__dirname}/public/files/${req.params.kind}/${req.params.fileName}`;
res.download(file);
});
app.listen(port, () => {
});
And my Vue (localhost:8080) component sends that request looks like this:
downloadVideo(fileName) {
const fileName = fileDir.split('/').pop();
const downloadUrl = `/download/videos/${fileName}`;
axios({
method: "get",
url: downloadUrl,
responseType: 'blob',
})
.then((response)=> {
// create file link in browser's memory
const href = URL.createObjectURL(response.data); // data is already a blob
// create "a" element with href to file & click
const link = document.createElement('a');
link.href = href;
link.setAttribute('download', 'my_video.mp4');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(href);
})
.catch((err) => {
// HANDLE ERROR HERE
})
},
I also have a vue config setup to proxy the requests to 8000:
// vue.config.js
module.exports = {
devServer: {
proxy: 'http://localhost:8000',
disableHostCheck: true
},
outputDir: '../backend/public', // build will output to this folder
assetsDir: '' // relative to the output folder
}
Instead of manually setting up the route for downloading files, you can directly set the static files directory as a controller and remove the app.get controller for downloading.
app.use(express.static("public/files"))
Then, on the client side, instead of downloading the file using JS, converting it into a data url, and then downloading it, you can do the following:
downloadVideo(fileName) {
// whatever you want to do to the file name
const parsedFileName = fileName
const link = document.createElement('a');
link.href = parsedFileName;
link.setAttribute('download', 'my-video.mp4'); // or pdf
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
Here is a sample working example
Im not really sure why the first request is going through. But the error looks like a CORS problem.
Basically, your frontend and backend run on different ports, which are treated like a different server altogether by the CORS checks.
I took the following config from the cors package docs
var express = require('express')
var cors = require('cors')
var app = express()
var corsOptions = {
origin: 'http://localhost:8080',
optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
}
/*
Or use this, if you want to bypass cors checks - not a safe practice
var corsOptions = {
origin: '*',
optionsSuccessStatus: 200
}
*/
app.get('/products/:id', cors(corsOptions), function (req, res, next) {
res.json({msg: 'This is CORS-enabled for only localhost:8080'})
})
app.listen(80, function () {
console.log('CORS-enabled web server listening on port 80')
})
The 200 ERR is funny, because normally 200 means OK. But in this case 200 refers to the Preflight request and ERR to the fact that the Ports are different.
There is a good video about cors on youtube

Node JS post API endpoint not recognized in front end

I'm trying to make a post request using appwrite SDK in Node JS express and Vue JS. The SDK requires me to create an api post request to create new storage bucket in appwrite. The DOCs for this particular request isn't explaining really how to create the api in node JS express. I'm really new to Node JS and I already succeeded at creating get request but whenever I create the post request I get 404 not found error.
Node JS express file (server.js):
In this file there is get users request API which works perfectly fine.
And there is create bucket post request which when being called in frontend it comes back with a 404
const express = require("express");
const path = require("path");
const app = express(),
bodyParser = require("body-parser");
port = 3080;
// Init SDK
const sdk = require("node-appwrite");
let client = new sdk.Client();
let users = new sdk.Users(client);
let storage = new sdk.Storage(client);
client
.setEndpoint("http://localhost/v1") // Your API Endpoint
.setProject("tailwinder") // Your project ID
.setKey(
"Secrer Key!"
); // Your secret API key
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, "../appwrite-app/build")));
//This get request works fine
//get user by ID
app.get("/v1/users/:id", (req, res) => {
let promise = users.get(req.params.id);
promise.then(
function (response) {
res.json(response);
},
function (error) {
console.log(error);
}
);
});
//This one isn't recognised in frontend
app.post("/v1/storage/buckets", function (req, res) {
let promise = storage.createBucket("bucket_id", "bucket_name", "file");
promise.then(
function (response) {
res.json(response);
},
function (error) {
console.log(error);
}
);
});
app.listen(port, () => {
console.log(`Server listening on the port::${port}`);
});
bucketsServices.js:
Here I'm using fetch post request to the api endpoint but it's not working.
export async function createBucket() {
const response = await fetch("/v1/storage/buckets", {
method: "POST",
});
return await response.json();
}
Addcomponent.vue:
Here I'm calling out the createBucket function from vue js file
bucketTesting() {
createBucket().then((response) => {
console.log(response);
});
},
The error which I assume it means that it's not reading my node js express post API:
bucketsService.js?993b:2 POST http://localhost:8080/v1/storage/buckets 404 (Not Found)
Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 0
A screenshot of the same error:
Something is missing here and I can't really figure it out.
You are making request to localhost:8080 meanwhile your server is running at localhost:3080
I believe your vue is running at port 8080 that's why /v1/storage/buckets gets prefixed by localhost:8080
Try to provide full URL while making request
export async function createBucket() {
const response = await fetch("localhost:3080/v1/storage/buckets", {
method: "POST",
});
return await response.json();
}
Better way might be to add proxy to automatically redirect request to correct URL, but this should work for now. This article might help with how to setup proxy in vue

Getting 404 when hitting API URL from react to node in production

I have a backend for the frontend framework for my app. On local, I am able to hit API URL from React to Node, but when I am deploying my app to prod, it is throwing me 404. I am currently using netlify to deploy my changes and checking them. Below is the code I am using in local to hit the API,
React-
const response = await fetch('http://localhost:5000/fetchData');
Node-
const express = require('express');
const cors = require('cors');
const axios = require('axios');
const port = 5000;
const app = express();
app.use(cors());
app.get('/fetchData', async (req, res) => {
const apiResponse = await axios.get(
'https://jsonplaceholder.typicode.com/posts'
);
res.send(apiResponse.data);
});
app.listen(port, () => {
console.log('Server is up');
});
While building this code for prod, I am removing http://localhost:5000 from the URL on React side and hitting the endpoint like this,
const response = await fetch('/fetchData');
But I am getting 404, could someone help where I am going wrong and how to hit the URL properly?
Your issue is that by using
const response = await fetch('/fetchData');
You are requesting using the frontend domain/ip.
So if the frontend domain was example.com, you are trying to access https://example.com/fetchData, I'm guessing you need to access https://api.example.com/fetchData. I'd recommend using an axios instance to add the base URL once only.

Downloading a file from a Node.JS API REST (express) with React.JS (Axios Get)

I have a React JS application that as a Backend has an API REST made with Node JS.
Currently, my objective is to be able to download files that are on the server.
The correct behavior should be that the user, after clicking on "Download file", should receive the file (Download with browser).
On the server-side, I have something like this (obviously, I'm gonna simplify it by removing JWT middleware, DB queries, etc..):
const express = require('express');
const router = express.Router();
const bodyParser = require("body-parser");
const cors = require("cors");
const app = express();
app.use(cors({ origin: "http://localhost:3000" }));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
router.get('/download', (req, res, next) => {
res.download("\\\\folder\\subfolder\\myfile.txt");
});
app.use('/api', router);
const PORT = 3001;
app.listen(PORT, function() {
console.log("Server is running on port "+PORT);
});
Then, as I have said, I have a React JS application working as a Frontend:
apihelperdownload () {
return axios.get(API_URL + "download").then(function (response) {
return response;
})
}
.............
function downloadFile() {
apihelperdownload().then(
(res) => {
// Should I do something here with response?
},
(error) => {
}
)
}
<button className="download" onClick={() => downloadFile()}>
Download File
</button>
I have two files on my server, one TXT and one JPG.
Both have the correct path (I'm not getting any "file/path not found" error) and I am receiving a "200 status OK" in both cases... But I can't download the files.
Also:
In the JPG case, in Network Tab, on preview sub-Tab I can see the image (so the browser is receiving the image).
And the response looks like this:
(ignore the params and the different url, it's just that here is not simplified)
- In the TXT case, in Network Tab, on preview sub-Tab I can just see a white page.
And the response looks like this:
As you can see, in this second case (.txt file), the data is "empty" ( "" )
Data is the correct text.. I didn't save the txt file.. So it was empty..
I have checked several related questions like this Download a file from NodeJS Server using Express
But unfortunately, I haven't found how to solve my issue.
1) What am I doing wrong on the server-side?
2) What I have to do with the response on client-side?
Thanks
I have found how to solve it without third-party libraries and in quite an "easy way".
First of all, I have changed the request to POST (since I just made GET because I thought it was the only way).
After that, on the Axios request, we have to indicate the responseType as blob:
function apihelperdownload () {
return axios.post(API_URL + "download",{ key: 'value', headers: authHeader(), responseType: 'blob' }).then(function (response) {
return response;
})
}
Then, when we receive the response, we have to create an URL object as a Blob and a link element to download it.
function downloadFile(filename) {
apihelperdownload().then(
(res) => {
const url = window.URL.createObjectURL(new Blob([res.data]));
const link = document.createElement('a');
link.href = url;
if (typeof window.navigator.msSaveBlob === 'function') {
window.navigator.msSaveBlob(
res.data,
filename
);
} else {
link.setAttribute('download', filename);
document.body.appendChild(link);
link.click();
}
},
(error) => {
alert("Something went wrong");
}
)
}
With this, we can download almost any kind of file very easily.
You can use js-file-download module.
const FileDownload = require('js-file-download');
Axios.get(API_URL + "download")
.then((response) => {
FileDownload(response.data, 'file.txt');
});
Check this response for more: https://stackoverflow.com/a/41940307/6512445

Resources