React Native - Sending a PDF with fetch but it is empty - javascript

Hey guys I have a question regarding the fetch function from JavaScript and the Package Nodemailer.
NOTE: I am using Expo.
I have a PDF created on my Emulator which looks good (filled with data). Now I want to send this PDF to my Backend and therer I want to send the File out as a PDF and get it in my Mails.
Frontend fetch-Call for my Backend:
async sendMonthReportMail(fileUri) {
await fetch(`${connectionString}/api/mails/sendmonthreport`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
senderUser: "My Name",
recieverMail: "myname#mymail.com",
pathMonthLock: `${fileUri}`
})
})
.then(res => res.json())
.then(res => {
console.log("Success SendMailMonthLock - " + res)
})
.catch(err => console.log("Error SendMailMonthLock - " + err))
}
I have also tried to use formData and change the Content-Type to "application/pdf" but this did not work.
Now my Backend Code looks like this:
router.post("/sendmonthreport", async (req, res) => {
const {recieverMail} = req.body;
const {senderUser} = req.body;
const {pathMonthLock} = req.body;
let transport = nodemailer.createTransport({
host: "mailserver",
port: xx,
secure: false,
tls: {
rejectUnauthorized: false
}
});
var message = {
from: "My Sendermail <sender#mail.com>",
to: `${recieverMail}`,
subject: `Mail comming from - ${senderUser}`,
text: `EmailText `,
attachments:[{
filename: "MyFilename.pdf",
content: __dirname + `./${pathMonthLock}`,
}]
}
await transport.sendMail(message)
.catch(err => console.log("Error Mail: " + err ))
res.send("email send")
})
Now the way that I choose the file.uri is threw the DocumentPicker for Expo.
The Problem I am having now is, that the Mail gets send correctly and there is a PDF attached to it with the correct name. However when I look at the PDF Size it is only 500Bytes and it is completly empty. When trying to open the File that is send via Mail then it tells me that the File is corrupted or wrong format.
How can I send the actual content that is inside the PDF?
When I try to use formData:
async sendMonthReportMail(file) {
fdPost.append("file", file);
fdPost.append("senderUser", "User Name");
fdPost.append("recieverMail", "username#mail.com");
console.log(fdPost);
await fetch(`${connectionString}/api/mails/sendmonthreport`, {
method: "POST",
headers: {
accept: "application/json"
// "Content-Type": "multipart/form-data",
// "Content-Type": "application/json",
// "Content-Type" : "application/pdf"
// "Content-Type": "application/x-www-form-urlencoded"
},
body: fdPost
})
.then(res => res.json())
.then(res => {
console.log("Success SendMailMonthLock - " + res)
})
.catch(err => console.log("Error SendMailMonthLock - " + err))
}
fdPost is defined at the top of my file, console.logging it gives me a valid FormData but the request ends with a Network Error!
Error SendMailMonthLock - TypeError: Network request failed
Also my Backend does not react to that call.
I also tried to use RN Fetch Blob but it only works in React Native only Projects. I am using Expo so this is not a soultion for me!
Edit:
I tried the suggestion in the comments (using Multer):
Frontend:
async sendMonthReportMail(file) {
fdPost.append("file", file);
fdPost.append("senderUser", "name");
fdPost.append("recieverMail", "mail#mail.com");
await fetch(`${connectionString}/api/mails/sendmonthreportTest`, {
method: "POST",
headers: {
Accept: "application/json",
'Content-Type': 'multipart/form-data'
},
body: fdPost
})
.catch(err => console.log("Error SendMailMonthLock - " + err))
}
Backend:
router.post("/sendmonthreportTest", upload.single("file"), (req, res) => {
console.dir(req.file + req.body);
})
Still I have the same issue! It gives me TypeError: Network Error.
Also when I try to call that point with Postman I am getting:
POST /api/mails/sendmonthreportTest 500 6.952 ms - 1221
TypeError: Cannot convert object to primitive value
To confirm my POST Body when I console.log the fdPost before the fetch:
{"_parts":[["file",{"size":69496,"name":"Monatsbericht_User_Name_Dezember2022.pdf","type":"success","uri":"file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540yxxx%252Fxxx/DocumentPicker/2xx07e4-4136-4b6f-ad70-26xxac64064.pdf","mimeType":"application/pdf"}],["senderUser","User Name"],["recieverMail","user#mail.com"]]}

Related

File Downloading Error Using Express, Axios and Js-File-Download

I made a simple downloadable response from my express server.
app.post("/download", (req, res) => {
let file_name = req.body.name;
res.download(path.join(__dirname, `files/${file_name}.mp3`), (err) => {
console.log(err);
});
});
And I used axios and js-file-download to download the responsed file from frontend. The file is donwloaded with full file size. But it's not playable.
Axios.post("http://localhost:3001/download", { name: name }).then(
(response) => {
fileDownload(response.data, `${title}.mp3`);
}
);
How can I solve this problem?
In my opinion, every Axios release from v1.0.0 has been fundamentally broken.
The request you're making is trivially easy using the built-in Fetch API and won't be subject to Axios' poor testing and broken releases.
fetch("http://localhost:3001/download", {
method: "POST",
body: JSON.stringify({ name }),
headers: { "content-type": "application/json" },
})
.then((res) => (res.ok ? res.blob() : Promise.reject(res)))
.then((file) => {
fileDownload(file, `${title}.mp3`);
});

File Uploading using Node js API

I am really stuck with this issue. I want to upload a file from this API to another API but this code does not seems working when I am sending the form data, it is going as raw content.
form.append('attachment[attachment]', req.files.meeting_attachment);
fetching the data by this way gives source.on is not a function
router.post('/attachments', async (req, res) => {
try {
var form = new FormData();
form.append('meeting_attachment[attachment]', Buffer.from(req.files.meeting_attachment.data), { filename: req.files.meeting_attachment.name });
const options = {
headers: {
'Content-Type': 'multipart/form-data',
'accept': '*/*'
}
};
const url = `https://webhook.site/a307cf34-at22-70b4-3a84-7ef36062d72c`;
const response = await axios.post(url, form, options).then((res) => {
console.log("result", res);
});
res.json({ status: true, data: response.data });
} catch (err) {
console.log("err", err);
res.status(404).json({ status: false, error: 'File type is not supported' });
}
});
I am getting 400 Bad Request with this raw content
Postman request to the actual receiver API looks like this on webhoook and it uploaded the file successfully.

Can not download file while sending request from one node.js server to another

I am facing some issue while downloading file using node.js. I have scenario like my angular component is sending the file request. in my first node server I am doing the token validation and then redirecting to another node server where actually the execution happens. I am explaining my code below.
service.ts:
submitAndDownloadFile(formdata : any ){
const token = localStorage.getItem('token')
let headers = new HttpHeaders({
Authorization: 'Basic ' + token
})
const cecID = localStorage.getItem('cec');
const AppUrl = `${environment.nodeJsBaseUrl}:${environment.hostingNodeJsContainerPort}/convert-test-cases/${cecID}`;
return this.httpClient.post(AppUrl, formdata, { responseType: 'blob', observe : 'response', headers : headers});
}
Here I am sending the request to my first node.js server which code has given below.
app.js(first:port-8000):
router.post('/convert-test-cases/:id', middleware.auth, (req, res) => {
try{
let postRequestOptions = {
url: '',
method: 'POST',
json: true,
headers: {},
body: {},
};
postRequestOptions.url = 'http:localhost:9000/convert-test-cases';
postRequestOptions.headers = {
'Content-Type': 'application/json',
};
postRequestOptions.body = req.body;
request(postRequestOptions, async (error, response, pathList) => {
if(error) {
console.log('error', error);
}else{
res.send(pathList);
}
})
}catch(e){
responseObj = {
status: 'error',
msg: 'Error occurred while processing your request',
body: null
}
return res.send(responseObj);
}
})
Here I am doing the token validation using middleware.auth and sending same request to another node.js file which code is explained below.
app.js:(second-port-9000):
router.post('/convert-test-cases', async (req, res) => {
try{
let response = await ctcCtrl.convertTestCase(req.body, req.files);
if(response.status == 'success'){
res.set('Access-Control-Expose-Headers','*, Content-Disposition');
return res.download(response.fileName,response.fileName);
}else{
return res.send(response);
}
}catch(e){
responseObj = {
status: 'error',
msg: 'Error occurred while processing your request',
body: null
}
return res.send(responseObj);
}
})
Here only I am doing some execution and downloading the file. If I am connecting angular to node-9000 its working fine but my requirement is first I have to connect to port-8000 to some token validation and after that I have to send same req.body and re.file to app.js which is running in 9000 using request module. As per my code its not working at all.

Javascript fetch(POST) to express server fails. The server does not receive the request from JS, but receives request from Postman

MRE -> node-server : react app
When I send a POST request using Postman, I get the expected result. This is the request that I am sending using Postman
and test sent gets printed to the console of my node server
If I send a request from my react form however, test sent does not print to the console, but the catch block of my fetch request get's executed and err is printed to the console of my react app, followed by {}.
I would like to know why my POST request is not working and is not getting received by the server
Below is the function that I call when someone clicks the submission button of my form created in react
Function called on form submission
nodeUrl = 'https://localhost:6060?'
const submitData = async () => {
fetch(nodeUrl, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({'test': 'test'})
}).then((res) => {
alert('then')
}).catch((err) => {
alert('err')
alert(JSON.stringify(err))
})
}
}
This is the server that I run using node server.js
server.js
server.post('/', function(req, res) {
console.log('test sent')
mailer.messages().send(req.body)
.then((mes) => {
console.log(mes)
res.json({ message: 'Thanks for your message. Our service team has been notified and will get back to you shortly.' })
}).catch(err => {
console.log(err)
res.json(err);
})
});
The majour issue here is due to CORS. CORS support can be used to overcome this. Just keep in mind to have this only for development mode(see below codes).
But, as per the Postman's snapshot and provided GitHub repositories, the request from Front-end should be of multipart/form-data type. Thus, the Front-end code would look like this
const nodeUrl = "http://localhost:6060/";
const submitData = async () => {
// create a FormData object
const formData = new FormData();
formData.append('form', 'example#email.com');
formData.append('to', 'example#email.com');
// this auto adds 'multipart/form-data' + HASH header in the request
fetch(nodeUrl, {
method: "POST",
body: formData
})
.then(res => {
console.log(res);
}).catch(err => {
console.log('Error -', err);
});
};
To handle multipart/form-data request in the ExpressJS, you need a plugin Multer.
const express = require('express');
const bodyParser = require('body-parser');
const multer = require('multer'); // for 'multipart' type request
const server = express();
const upload = multer();
// allow CORS requests in development mode
if (process.env.NODE_ENV === 'development') {
// Server run command - "NODE_ENV=development node server.js"
const cors = require('cors');
server.use(cors());
}
server.use(bodyParser.json());
server.use(bodyParser.urlencoded({extended: true}));
// using Multer middleware form extracting 'FormData' payload
server.post('/', upload.none(), function(req, res) {
console.log('Received body', req.body);
... // other codes
});
Strategy 2(plain JSON) -
If that 'multipart/form-data' strategy was unintentional and you just want to send simple JSON, use below codes -
In Front-end, trigger API request as -
fetch(nodeUrl, {
method: "POST",
headers: {
'Content-Type': 'application/json', // this needs to be defined
},
body: JSON.stringify({ from: 'some#email.com', to: 'other#email.com' })
})
In server, just ignore codes related to Multer and only keep your API as -
server.post('/', function(req, res) {
console.log('Received body', req.body);
... // other codes
});
I ended up using a better fetch request, which was put together for me by selecting code -> Javascript Fetch in Postman(under the save button)
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
var urlencoded = new URLSearchParams();
urlencoded.append("from", "example#email.com");
urlencoded.append("test", "test");
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: urlencoded,
redirect: 'follow'
};
fetch("http:localhost:6060/, requestOptions)
.then(response => {
if (response.ok){
response.json().then(json => {
console.log(json)
})
}
})
.catch(error => console.log('error: ', error))

Svelte/Sapper: Body empty on POST

I'm trying to create a login form using sapper, but am encountering the following problem when trying to test a basic POST fetch.
In routes/login/login.svelte, I have the following code which is called on a button click:
<script>
let data = {"email":"test"};
const handleLogin = async () => {
const response = await fetch("/login/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: data
});
};
</script>
Which should send what is in data to routes/login/login.js which has the following code:
export async function post(req, res, next) {
res.setHeader('Content-Type', 'application/json');
var data = req.body;
return res.end(JSON.stringify(data));
}
My problem is that this only returns {} rather than the data sent in the svelte page. Any ideas as to why this is happening and where I'm going wrong? Thanks.
When sending the data, you should also stringify it there
body: JSON.stringify(data)
as an extra make sure you have body-parser installed and added as middleware in the server, this package will help you handle requests that have send json data in their body.
polka() // You can also use Express
.use(
compression({ threshold: 0 }),
sirv('static', { dev }),
bodyparser(),
sapper.middleware()
)
.listen(PORT, err => {
if (err) console.log('error', err);
});
Building on the previous answer, I'm writing here the full working solution. Your problems may be due to:
Not using the json parse middleware
Not treating fetch as a promise
Here's how I'd fix it:
npm i body-parser
Add the json middleware in your server.js
const { json } = require('body-parser');
polka()
.use(
compression({ threshold: 0 }),
json(),
sirv('static', { dev }),
sapper.middleware()
)
.listen(PORT, err => {
if (err) console.log('error', err);
});
Treat the fetch response as a Promise. This is how your Svelte component should look like (notice the chained then):
<script>
let data = {"email":"test"};
const handleLogin = async () => {
await fetch(`your-endpoint`, {
method: 'POST',
body: JSON.stringify(data),
headers:{
'Content-Type': 'application/json'
}
})
.then(res => res.json())
.then(res => console.log(res)); // {email: "test"}
};
</script>

Categories

Resources