Ok, so i need to proxy file array upload from my express node app, to remote PHP Api.
Ideally i would use something close to Nginx proxy since it has same modules with node.
If not,i would emulate form resend.
Can you help find the best way for doing this?
So, i did not found execaly how to proxy request itself, as it would do nginx, but at least i figured out how to redirect request with all it's data to different source.
So, here we use express-fileupload to get our file data from req, and form-data to create a form and send data.
import app from '#app';
import { authMw as checkJwtAndAddToRequest } from '#middleware/Auth';
import { Router } from 'express';
import fileupload, { UploadedFile } from "express-fileupload";
import FormData from 'form-data';
//add this MW to add files to you'r req
app.use(checkJwtAndAddToRequest)
app.use(fileupload());
app.post('/upload', (req, res) => {
const uploadableFiles: UploadedFile[] | UploadedFile | undefined = req.files?.formFieldName;
if(!uploadableFiles) {
throw Error('Pls add files to upload them');
}
//Transorm single file to same form as an array of files
const files: UploadedFile[] = Array.isArray(uploadableFiles) ? uploadableFiles : Array<UploadedFile>(uploadableFiles);
//create form
const form = new FormData();
//add files
files.forEach(
file => form.append('files[]', file.data.toString(), file.name)
)
//Submit
form.submit({
protocol: 'http:',
host: process.env.API_URL,
path: '/api-path-for/file/upload',
method: 'POST',
headers: {
//Add auth token if needed
authorization: `Bearer ${String(req.body.jwtToken)}`
}
}, (err, response) => {
if(err) {
//handle error
res.status(503).send('File server is currently unavailable');
}
//return remote response to original client
response.pipe(res)
});
});
Related
I'm using ReactJS to run my front-end and using Express for my back-end. I want to make a get request to my back-end using the "/paas" path to get a listing of all of my pods that are running inside my namespace in Rancher(Kubernetes).
The back-end then needs to be able to make an https request to my Rancher API endpoint and return the result to the front-end. I can make the successful call to Rancher API and see the data print to the screen on my back-end but I get lost when trying to send this data to the front-end and console log it out inside the browser.
Due to "pre-flight" errors, I can't just make a direct call to the Rancher endpoint inside of my App.js file. More info on this here. So I need to go the custom back-end route. I any case, it seems like this should be pretty straightforward. Any guidance would be appreciated.
App.js:
import React, { useEffect } from "react"
import axios from "axios"
function App() {
useEffect(() => {
const fecthPods = async () => {
try {
const response = await axios.get(`http://localhost:3001/paas`)
console.log(response.data)
} catch (err) {
if (err.response) {
// Not in the 200 response range
console.log(err.response.data)
console.log(err.response.status)
console.log(err.response.headers)
} else {
console.log(`Error: ${err.message}`)
}
}
}
fecthPods()
},[])
return (
<div>
Hello World!
</div>
);
}
export default App;
Back-end server.js:
import express from "express"
import cors from "cors"
import https from "https"
import bodyParser from "body-parser";
const app = express()
app.use(cors())
app.use("/data", (req, res) => {
res.json({ name: "Minion", favFood: "pizza"})
})
app.get("/paas", bodyParser.json(), (req, res) => {
const options = {
hostname: "k8.fqdn.com",
port: 443,
path: "/k8s/clusters/c-wwfc/v1/pods/mynamespace",
method: "GET",
headers: {
Authorization: "Bearer token:12345"
}
}
const request = https.get(options, (res) => {
let responseBody = ""
res.setEncoding("UTF-8")
res.on("data", (chunk) => {
console.log("---chunk", chunk.length);
responseBody += chunk;
});
res.on("end", () => {
let json = JSON.parse(responseBody)
// console.log(responseBody)
console.log("Response finished");
res.json({data: responseBody})
});
});
request.end()
res.json({ status: "complete", data: request.data})
})
app.listen(3001)
console.log("backend up on 3001")
I see a couple of errors on your backend code.
First, you are naming the res variable for the express middleware and also for the response received by the https module. In this way, you lose the possibility to access to the express response object in the on.('end') callback.
Secondly, you are triyng to respond to the client multiple times (inside the on.('end') callback and also directly inside the express middleware with the instruction res.json({ status: "complete", data: request.data}). Also, consider that the code you wrote is repliyng to the client before the call to the k8s cluster is made. And the response will always be a JSON with this data: { "status": "complete", "data": undefined}.
To fix all, try with this code (I will try to comment all edits):
app.get("/paas", bodyParser.json(), (req, res) => {
const options = {
hostname: "k8.fqdn.com",
port: 443,
path: "/k8s/clusters/c-wwfc/v1/pods/mynamespace",
method: "GET",
headers: {
Authorization: "Bearer token:12345"
}
}
const k8sRequest = https.get(options, (k8sResponse ) => { // as you can see I renamed request and res to k8sRequest and k8sResponse, to avoid loosing the scope on req and res express middleware variables
let responseBody = ""
res.setEncoding("UTF-8")
k8sResponse.on("data", (chunk) => { // here use k8sResponse to collect chunks
console.log("---chunk", chunk.length);
responseBody += chunk;
});
k8sResponse.on("end", () => { // here use k8sResponse again
let json = JSON.parse(responseBody)
// console.log(responseBody)
console.log("Response finished");
res.json({ status: "complete", data: responseBody}) // here use the express res variable, to reply to the client.
});
});
k8sRequest.end() // here use the k8sRequest variable to make the https call to the k8s cluster
// here I deleted the res.json instruction
})
The above code should just works. Anyway, I suggest you using axios also with your backend service. You are already using it with React, so you know how to use it. The syntax is minimal and easier and you can use the async/await approach.
Axios solution:
import axios from "axios"
app.get("/paas", bodyParser.json(), async (req, res) => {
try {
const url = 'https://k8.fqdn.com/k8s/clusters/c-wwfc/v1/pods/mynamespace'
const k8sResponse = await axios.get(url, headers: {
Authorization: "Bearer token:12345"
})
res.json({ status: "complete", data: k8sResponse.data })
} catch (e) {
res.json({status: "error", data: e.response.data})
}
})
You should wrap your axios call inside a try/catch block to properly handle errors like you are doing with your React implementation. Error handling should be also implemented if you still want you the native node.js https module
I am struggling with a simple media (mp3/mp4) upload to a server using axios.
I have an angular application that creates a formData and send this formData to node server via :
return this.http.post(this.apiURL + '/uploadFile', formData);
My server method looks like this :
app.post('/api/uploadFile', upload.single('file'), (req, res) => {
inputFile = req.file;
let fd = new FormData();
fd.append('file',inputFile.buffer, inputFile.originalname);
axios.post(uploadFileURL , fd, { headers: { 'Content-Type': 'multipart/form-data' } })
.then((response) => {
console.log(response);
})
.catch((error) => {
console.error(error)
})
})
The inputFile contains the original files. The error I get now is that the request is not a multipart request...
I tried as well to define the formData differently :
formData = {
file: {
value: inputFile.buffer,
options: {
filename: inputFile.originalname,
contentType: inputFile.mimetype
}
}
};
Which brought me to a different error : 'Failed to parse multipart servlet request; nested exception is java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found'
Am I doing something wrong ?
I am wondering if this could be link to the fact that I use const bodyParser = require('body-parser'); for some of my other requests.
Any help would be appreciated!
Thanks
EDIT :
Here is my need and what I've done so far :
I have a web application that allow users to upload media files.
I have to send those files to a server, but I can not use the browser to send the request directly.
I created a nodejs application to realize the proxy task of getting the files from the browser and sending it to my remote server.
I have the snipped in NodeJS to try to do a download from my server.
router.post("/download", function(req, res, next) {
console.log(req);
filepath = path.join(__dirname, "/files") + "/" + req.body.filename;
console.log(filepath);
res.sendFile(filepath);
});
This is my Angular 2 code:
Component.ts
download(index) {
var filename = this.attachmentList[index].uploadname;
this._uploadService
.downloadFile(filename)
.subscribe(data => saveAs(data, filename), error => console.log(error));
}
Service.ts
export class UploadService {
constructor(private http: HttpClient, private loginService: LoginService) {}
httpOptions = {
headers: new HttpHeaders({
"Content-Type": "application/json",
responseType: "blob"
})
};
downloadFile(file: string): Observable<Blob> {
var body = { filename: file };
console.log(body);
return this.http.post<Blob>(
"http://localhost:8080/download",
body,
this.httpOptions
);
}
In my HTML response I get the file correctly recording to my browser's network inspection tool. However Angular is trying to parse the response into a JSON object which obviously doesn't work since the response is a blob.
The error I get is this:
Unexpected token % in JSON at position 0 at JSON.parse ()
at XMLHttpRequest.onLoad angular 2
Does anyone have a solution for this?
Thanks in advance!
Try the following http options:
{
responseType: 'blob',
observe:'response'
}
You will then get a response of type HttpResponse<Blob>, so doing data.body in your service will give you the blob that you can pass to saveAs.
You can find more info on observe here:
https://angular.io/guide/http#reading-the-full-response
I solved that, in service code I deleted the after the post because it forced my return get a binary file, and not a JSON. Now the code it's like that:
downloadFile(file: string): Observable<Blob> {
var body = { filename: file };
console.log(body);
return this.http.post<Blob>(
"http://localhost:8080/download",
body,
this.httpOptions
);
}
Thank you all.
I need to send BLOB to the server in order to make an image on same.
I am using axios on reactJs client and sending data by using this code.
/**
* Returns PDF document.
*
*/
getPDF = (blob) =>
{
let formatData = new FormData();
formatData.append('data', blob);
return axios({
method: 'post',
url: 'http://172.18.0.2:8001/export/pdf',
headers: { 'content-type': 'multipart/form-data' },
data: {
blob: formatData
}
}).then(response => {
return {
status: response.status,
data: response.data
}
})
}
I tried to console.log this blob value on client and there is regular data.
But on server request body is empty.
/**
* Exports data to PDF format route.
*/
app.post('/export/pdf', function (request, response) {
console.log(request.body.blob);
response.send('ok');
});
If I remove headers still empty body when sending blob, but if I remove blob and send some string, a server receives data.
But when the blob is sent server has an empty body.
NodeJS natively does not handle multipart/form-data so you have to use external module eg :- multer
Code Example(Not Tested):
var upload = multer({ dest: __dirname + '/public/uploads/' });
var type = upload.single('upl');
/**
* Exports data to PDF format route.
*/
app.post('/export/pdf', type, function (request, response) {
// Get the blob file data
console.log(request.file);
response.send('ok');
});
you can read about multer here
I hope this will work for you.
Are you using body-parser?
body-parser doesn't handle multipart bodies, which is what FormData is submitted as.
Instead, use a module like multer
let multer = require('multer');
let upload = multer();
app.post('/export/pdf', upload.fields([]), (req, res) => {
let formData = req.body;
console.log('Data', formData);
res.status(200).send('ok');
});
I had 2 problems that I had to solve for this. 1 firebase functions has a bug that doesn't allow multer. 2 you may be getting a blob back from response.blob() and that doesn't seem to produce a properly formatted blob for firebase functions either.
I am using react(frontEnd) with nodejs (backend), I am trying to send post request from client to sever. .
Client Side
Here below I sending the request to server, by using axions.
I tried without axions simply calling fetch instead.I tired with adding localhost:5000 and without but I it still not working.
FYI , all the data that I am sending are valid and there are as requested.
Server
Here I am trying to handle my request and at least print it to console to verify that my request is as passed valid
result of prints!!!
could you please advise regarding the above ?
After adding body-parser:
currently I am getting a undefined object, could you please advise?
Try This way
const data = {
name: this.state.additionalCost
};
axios
.post(
'/api/processData',
data,
{ headers: { 'Content-Type': 'application/json' } }
)
.then(function(result) {
console.log(result);
});
And use body-parser in express app
import express from 'express';
import bodyParser from 'body-parser';
app.use(bodyParser.json());
Set content type in header and check
axios.post('url', data, {
headers: {
'Content-Type': 'application/json',
}
}
You don't need to JSON.stringify your data from the react axios request.
Here's an example:
const user = {
name: this.state.name
};
axios.post(`https://jsonplaceholder.typicode.com/users`, { user })
.then(res => {
console.log(res);
console.log(res.data);
}
)
For more read Using Axios with React.