I want to realize uploading files for my users. I use CKEDITOR 5 in my react project. Back-end on nodeJS.
So, i can upload file, can get its Url, but, can't display one in VIEW page.
//my server code
const express = require('express');
//for uploading i use this module
const multiparty = require('connect-multiparty');
const multipartyMiddleware = multiparty({uploadDir: '/var/www/group0384.ru/public_html/server/uploads'}) //here is whole path to my upload folder on server
const app = express();
const port = 3555;
const path = require('path');
const moment = require('moment');
const fs = require('fs');
//so, here i have route /upload, which is indicated in configuration of ckeditor as route to send pictures
app.use(express.static("uploaded"));
app.post('/upload', multipartyMiddleware, (req, res) => {
var TempFile = req.files.upload;
var TempPathfile = TempFile.path;
const targetPathUrl = path.join(__dirname,"./uploaded/"+TempFile.name);
if(path.extname(TempFile.originalFilename).toLowerCase() === ".png" || ".jpg"){
fs.rename(TempPathfile, targetPathUrl, err =>{
res.status(200).json({
uploaded: true,
url: `${__dirname}/uploaded/${TempFile.originalFilename}`
}); // this path is the same as in 5th row (except folder, here it change, but it's no matter)
if(err) return console.log(err);
})
}
})
//------------CKEDITOR CODE---//
<CKEditor
editor={ClassicEditor}
data={this.state.data}
onChange={(event, editor) => {
this.setState({
data: editor.getData(),
});
}}
config={
{
ckfinder: {
uploadUrl: '/upload'
} // here, /upload - the route to send pictures
}
}
/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
On my VIEW page, i getting this
screenshot
So, i've tried to change paths, but still couldn't get the picture.
please explain why I can't just get and output a file that is already uploaded on my own server
P.S. Sorry for my english
It seems from the screenshot that you are getting the absolute path to the image, if you want to show the image on the client-side and you are sure the image is saved on your server, you have to send it back as a public URL address of your image!
example: "http://example.com/images/image1.png"
Thank you all for answers, i resolved the problem.
In this part i change url for uploaded images
res.status(200).json({
uploaded: true,
url: `/files/${TempFile.originalFilename}`
});
Then, i created route with this url
app.get('/files/:url(*)', (req, res) => {
console.log(req.params.url)
res.sendFile(path.resolve(__dirname + '/uploaded/' + req.params.url))
})
And it works!
Related
I am using gridfs and mongoDB to store images in chunks. And whenever a user requests my server just sends a streamlined image file in response by piping.
Currently my code looks like this :
const download = async (req, res)=>{
try{
const fileName = req.params.name
await mongoClient.connect();
const database = mongoClient.db(dbConfig.database)
const bucket = new GridFsBucket(database, { // required for important methods like openDownloadStream
bucketName:dbConfig.imgBucket
})
const downloadStream = bucket.openDownloadStreamByName(fileName);
downloadStream.pipe(res) // it only displays an jpg/png image
// res.render("image") , I want to render this ejs page with a image in it and with some static content in it. I want to stream image
} catch(err){
res.status(501).render("error",{error: err})
}}
My output looks like :
my code output
It only renders a jpg file, like how above link is working see it.
but rather what i want to do is to get image from response object and render it with my other html elements.
Looks like you're trying to do too much at once.
You need to de-couple the desired streaming image from the initial rendering of your template. Include an image tag in your tempalte with a distinct api from which the image will stream, and the result will look something like your example.
Say your image is called test.png, your server index.js, and your ejs teplate index.ejs. The template (for the sake of your question) can be very simple: index.ejs-->
<h1> stream that image below! </h1>
<image src="/image" hieght="200px" width="200px";/>
Notice the src of this image - this will hit a distinct api on your backend that will stream the image.
The server index.js will look like this -->
var exp = require("express");
var fs = require("fs");
var app = exp();
app.set("view engine", "ejs");
app.get("/image", (req, res) => {
const streamReadFile = fs.createReadStream("test.png");
streamReadFile.pipe(res);
});
app.get("/", (req, res) => {
res.render("index");
});
app.listen(8080, () => {
console.log("listening on *:8080");
});
Notice here at the home route you render the template as
app.get("/", (req, res) => {
res.render("index");
});
The src in the image of your template then makes a request back to the server, hitting the /image route which will stream the desired image to your html
app.get("/image", (req, res) => {
const streamReadFile = fs.createReadStream("test.png");
streamReadFile.pipe(res);
});
Here's a working demo of the example above, where your image is streamed to an ejs template.
for storage space issues i cannot save images in server so i had to store it in cloudinary
and for seo purposes I had to serve it from my domain not cloudinary's
so i thought to get img files from cloudinary and send it directly to browser (to be served from my domain name )
what i am missing is converting the img file i got from cloudinary api into the right form so i can send it using response object in nodejs
here is the code
app.get('/uploads/img/:imgName', (req, res) => {
axios.get('https://res.cloudinary.com/dkhccaa25/image/upload/blog_img/${req.params.imgName}')
.then(response => {
console.log(response);
/* how to convert response into the right format so it can be sent */
//
//
//
})
.then (response => {
/*converted response */
res.sendFile(response)
})
.catch(error => {
console.log(error);
});
how I can be able to send the file from node server to browser so it can be displayed using
<img src="img url...">
You do not have to use res.sendFile, this will require saving it to the filesystem. Basically - accept the response and pass it directly with the correct content-type header send by the upstream response to the client.
Minimal example:
const express = require('express');
const axios = require('axios');
const app = express();
app.get('/', (req, res) => {
axios.get('https://static.pexels.com/photos/45201/kitty-cat-kitten-pet-45201.jpeg').then((axiosResp) => {
res.header('content-type', axiosResp.headers['content-type']).send(axiosResp.data);
});
});
app.listen(3000);
finally the problem solved by editing on #madflow answer (thanks for him )
const express = require('express');
const axios = require('axios');
const app = express();
app.get('/', (req, res) => {
axios.get('https://static.pexels.com/photos/45201/kitty-cat-kitten-pet-45201.jpeg', {responseType: 'stream'})
.then((axiosResp) => {
res.set({
'Content-Type': axiosResp.headers['content-type']
})
axiosResp.data.pipe(res)
});
});
app.listen(3000);
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
I've built a basic browser form allowing users to upload a PDF file. I then want to send that file to an Express backend. It seems like this should be a pretty basic action, but I'm unfamiliar with the end-to-end process so I'm not sure which piece is failing. I've searched through a number of SO questions/answers, but haven't found any that provide a complete solution, and I haven't been able to cobble together a solution either.
Update: It looks like the file is getting to the server, but the encoding is messed up. My guess is that FileReader.readAsText is the wrong method to use. FileReader.readAsBinaryString got me a little closer, but still not quite right (and it's deprecated). FileReader.readAsArrayBuffer seems like potentially the way to go, but I'm not sure how to correctly handle the buffer in Express.
Client/Browser
The form is built in React and just uses an onChange handler on the input itself. When a file has been added, the handler reads the file, adds it to the form data and sends the post request to the server.
// React form
<input
name="upload"
onChange={this._handleUpload}
type="file"
/>
_handleUpload = (e) => {
const { files, name } = e.target;
// Read the file
const reader = new FileReader();
reader.onload = (e) => {
const file = e.target.result;
// Now that we have the file's contents, append to the form data.
const formData = new FormData();
formData.append('file', file);
formData.append('type', name);
axios
.post('/upload', formData)
.then(res => {
// Handle the response...
})
.catch(err => console.log(err));
};
// Reading as text. Should this be something else?
reader.readAsText(files[0]);
}
Express App
The express app uses multer middleware to process the upload:
const app = express();
const upload = multer({});
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cors());
app.post('/upload', upload.any(), handleUpload);
Middleware
Finally, I have my own middleware that gets the file from multer. I'm testing this piece by just writing the file I've received to disk. It has contents, but it's not a readable PDF file.
const handleUpload = (req, res, next) => {
// The file shows up on req.body instead of req.file, per multer docs.
const { file } = req.body;
// File is written, but it's not a readable PDF.
const tmp = fs.writeFileSync(
path.join(__dirname, './test.pdf'),
file,
);
}
Is there some piece that I'm getting obviously wrong here? eg: Do PDFs need to be handled in a special way? Any tips for where to focus my debugging?
See if that solves your problem.
_handleUpload = (e) => {
const dataForm = new FormData();
dataForm.append('file', e.target.files[0]);
axios
.post('http://localhost:4000/test', dataForm)
.then(res => {
})
.catch(err => console.log(err));
}
render() {
return (
<div className="App">
<input
onChange={this._handleUpload}
type="file"
/>
</div>
)
}
server:
router.post('/test', upload.any(), (req, res) => {
console.log(req.files)
res.send({sucess: true})
})
No need to send the file type, the multer identifies the name and type for you.
I've been struggling with this for a while and can't seem to find an answer, I'm developing a website with a budgeting option, I'm sending an object from the client to the server, and that server is using PDFKit to create a PDF version of the budget, once it's created I want to actually send back that PDF to the client and trigger a download, this is what I've done
Client-side code:
let data = {
nombre: this.state.name,
email: this.state.email,
telefono: this.state.phone,
carrito: this.props.budget.cart,
subTotal: this.props.budget.subTotal,
IVA: this.props.budget.tax,
total: this.props.budget.subTotal + this.props.budget.tax
}
axios({
method: 'post',
url: 'http://localhost:1337/api/budget',
data: data
})
.then((response) => {
console.log('This is the response', response);
window.open('/download')
})
.catch((error) => {
alert(error);
})
So that data goes to my server-side code perfectly and it looks like this
const pdf = require('pdfkit');
const fs = require('fs');
const path = require('path');
exports.makePDFBudget = (req, res) => {
let myDoc = new pdf;
myDoc.pipe(fs.createWriteStream(`PDFkit/budget.pdf`));
myDoc.font('Times-Roman')
.fontSize(12)
.text(`${req.body.name} ${req.body.phone} ${req.body.email} ${req.body.cart} ${req.body.subTotal} ${req.body.total} ${req.body.tax}`);
myDoc.end()
}
That's creating my PDF, what I want now is that once it's created and the response is sent back to the client, the client opens a new window with the URL "/download" which is set to download that PDF, but that's not happening for some reason, it opens up the new window but the download never starts and it throws absolutely no error I'm my Node console or browser console
this is how I send my file to the client
const fs = require('fs');
const path = require('path');
exports.downloadPDFBudget = (req, res) => {
res.download(__dirname + 'budget.pdf', 'budget.pdf');
}
And this is how my server index looks like
const bodyParser = require('body-parser');
const express = require('express');
const app = express();
const api = express.Router();
const { makePDFBudget } = require('./PDFkit/makePDFBudget.js');
const { downloadPDFBudget } = require('./PDFkit/downloadPDFBudget.js')
app.use(express.static(__dirname + '/../public'));
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json({extended: true}));
api.route('/budget')
.post(makePDFBudget)
api.route('/download')
.get(downloadPDFBudget)
app.use('/api', api);
const port = 1337;
app.listen(port);
console.log('Listening on port ', port);
module.exports = app;
I just solved it, the port in which I was running my client obviously was different from the one I was running my server, so I had to open a window to my server's port to trigger the download, I realized this because I threw a console log on the function that was supposed to do the res.download it wasn't showing up. Thanks!
I guess the main problem here:
res.download(__dirname + 'budget.jpg', 'budget.pdf');
Make a correct file name. Your file is pdf, not jpg.
At this code res.end(Buffer.from('budget.pdf')) you sending string, not file content. But headers like you want to send a file.
The last. Your application designed like you will have only one user. Could you add userId to file names? Or use DB for storing data and generate pdf on request without storing a file to the file system.