I am using node.js and the express framework to send a get request for an image by appending the image to the body using $('body').append('<img src="images/image.gif?34567">'). When I send the request, the console is logging GET /images/image.gif?34567 200 1.223 ms, but it won't run the functions inside of my router for the route to that image.
router.get('/images/*', function(req, res) {
console.log('Accessed image folder...')
var requestURL = url.parse(req.url,true);
//ATTEMPT to capture request
if(requestURL.pathname == '/images/image.gif') {
console.log("Fetching image...")
}
});
I was also trying to use the specific route: router.get('/images/image.gif', function(req, res) {, and tried following this example.
How can I make the GET router work when requesting a specific image inside of the images directory?
router.get('/images/:imageName', function(req, res) {
var image = req.params['imageName'];
res.header('Content-Type', "image/gif");
fs.readFile(image, 'utf8', function(err, data){
if(err){
res.end(404);
}
res.send(data)
});
});
Just grab the image name as a parameter, set the content type (you could also do this dynamically based on the requested file extension, for simplicity I only set it to gif), then do an async file load for the image name and return it.
If you are trying to view a image from node server, then you have to use express static . The following code might help you.
var express=require('express'),
app=express();
app.use("/pictures", lib.express.static("./assets/images"));
Here '/pictures' is the route to get image(e.g http:\localhost\pictures) and '/assets/images' is actual path foe image.
If you want to upload the image then you should use any upload module of node as per your requirement.
Related
I want upload the content of an excel file into the server in order to get its data and do some stuff...
I came up with the following code, however it seems like it is not working properly as the following error displays in the console Error: Can't set headers after they are sent.
The file is getting uploaded into the folder and the json message is being displayed... However I do not know if I am going to face any issue in the future...
Actually I just need the excel data no need for the excel being uploaded... Maybe you could give me a workaround, guys...
const router = express.Router();
const storage = multer.diskStorage({
destination(req, file, cb) {
cb(null, 'uploads/');
},
filename(req, file, cb) {
cb(
null,
`${file.fieldname}-${Date.now()}${path
.extname(file.originalname)
.toLowerCase()}`
);
},
});
const excelFilter = (req, file, cb) => {
if (
file.mimetype.includes('excel') ||
file.mimetype.includes('spreadsheetml')
) {
cb(null, true);
} else {
cb('Please upload only excel file.', false);
}
};
const upload = multer({
storage,
fileFilter: excelFilter,
});
router.post('/', upload.single('file'), (req, res) => {
var workbook = XLSX.readFile(req.file.path);
var sheet_name_list = workbook.SheetNames;
var xlData = XLSX.utils.sheet_to_json(workbook.Sheets[sheet_name_list[0]]);
res.json(xlData).sendFile(`/${req.file.path}`, { root: path.resolve() });
});
May I have a res.json and res.sendFile together in the same api endpoint in express?
No, you cannot. Each of those methods, sends a complete http response (including calling res.end() which terminates the http request) and you can only send one http response to each incoming request. The particular error you're getting has to do with the res.sendFile() trying to configure the response that it's getting ready to send and finding that the http response object has already been used for sending a response and can't be used again.
Ordinarily, if you wanted to sent two different pieces of data, you would just combine them into a single Javascript object and just call res.json() on the object that contains both pieces of data.
But, sending a binary file is not something you can easily put in a JSON package. You could construct a multipart response where one part was the JSON and one part was the file. You could JSON encode binary data (though that's inefficient). I presume there are probably some modules that would help you do that, but for most clients, that isn't what they are really expecting or equipped to handle.
The only way to a proper solution is for us to understand what client/server workflow you're trying to implement here and why you're trying to send back the same file that was just uploaded. There would normally not be a reason to do that since the client already has that data (they just uploaded it).
I am working on this single page app where I send the App.html file to the user after login and then I want to fill that file with the data from the database.
I cannot find a way to simply do 2 get requests at the same time since one is for the file the other is for the data.
any work arounds ? as I have been reading you cannot send 2 requests for the same URL.
and since serving the file is eliminating my response window how can I implement this then?.
I want to stay on the same page so different route will not work because it's going to redirect me somewhere else and I will lose my access to the App.html that is already in the browser.
I also read that there is an option parameter in send file but it hasn't been working for me.
// Express Code
//Controler File
exports.getApp = async(req, res, next) => {
//app
res.sendFile(await path.join(__dirname, '../', 'views', 'App.html'))
}
exports.getData = async (req, res, next) => {
//data
const getData = await DB.find()
res.json(getData)
}
// Routes File
app.route('/app')
.get(toAppControler.getApp)
.get(toAppControler.getData)
I'm trying to get image (jpg file) from node.js and display it in html tag (img), but the picture is not shown (as you can see:).
My node.js which handle the request looks:
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended : true}));
app.get('/pictureItem', function (req, res) {
// imagesNames[0].name - contains the name of the image to send (jpg file)
res.sendFile('/images/' + imagesNames[0].name, {root: __dirname })
})
and my js code looks:
$.get("/pictureItem", function(data, status)
{
console.log("got image");
$("#imageId").attr("src", data);
})
what am I missing ?
Your '/pictureItem' route in Express sends an image.
Your ajax call in your client code seems to be expecting to get an URL back, not an image:
$.get("/pictureItem", function(data, status) {
console.log("got image");
$("#imageId").attr("src", data);
});
The usual way you would do this is:
Set $("#imageId").attr("src", data); to an URL that your server knows how to serve the image for.
This will cause the browser to then request that URL from your server.
When your server gets that image request, it will send the image back.
The browser will display the image it got back from the server.
I'm not quite sure what the overall problem is you're trying to solve here (you don't show the overall logic of the operation), but you could just do this:
$("#imageId").attr("src", "/pictureItem");
And, then your existing server route would return the desired image when the browser requests the image from the /pictureItem route.
Have you defined the middleware for static files? If yes you could define a GET method which retrieves the path to a specific image and return that so you can attach it to the src attribute.
Your pictureItem endpoint is return the image file itself. Whereas your src attribute expects a string so it can fetch the image itself.
server.js
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended : true}));
app.use('/images', express.static(__dirname + '/Images'));
app.get('/pictureItem', function (req, res) {
// returns a string as '/images/someimagename.jpg'.
res.send('/images/'+imageNames[0].name);
});
someJavascript.js
$.get("/pictureItem", function(data, status)
{
console.log("Found image on url: " + data);
$("#imageId").attr("src", data);
});
Sorry, I tend to be a bad writer when I have not fully woken up, let me revise.
I am using expressjs with passportjs (local strategy) to manage my server and using connect-busboy to manage file uploading. I do not think passport will play a role in this.
Here is the server code for managing file uploads:
app.post('/upload', isLoggedIn, (req, res) => {
if(req.busboy){
req.pipe(req.busboy);
req.busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
if(mimetype.match(/^image\//)){
var root = path.join(__dirname, "../public/images/");
if(fs.existsSync(path.join(root, filename))){
var name = getUnique(path.join(root, filename));
} else {
var name = filename;
}
var ws = fs.createWriteStream(path.join(root, name), { flags: "a" });
file.pipe(ws);
}
});
}
});
As for my client page, it is used to change a JSON object which will get re-uploaded to the server as a configuration tool. When I upload a new image asynchronously I need to get the filename to update this JSON object while working on it. For uploading from the clients end I am using dropzonejs, which did not require any configuration on my part to work.
So, in summary I upload a number of images via dropzone asynchronously, busboy and fs on my server save the file, and I would like to get the filename returned to my javascript to modify the existing JSON object.
Edit solution:
Thanks to Elliot Blackburn for pointing me in the right direction.
By calling:
ws.on('close', () => {
res.send({filename: name});
});
after file.pipe(ws); to send the response back to the client. On the client side modify dropzone to handle the response like so:
dropzone.on('success', (file, res) => {
console.log(res);
});
Just send it in the normal http response. It'll depend what library you're using but most will allow you to trigger a normal req, res, next express call. From that you can access the file object, and return anything you want.
Something like:
req.send({filename: name}); // name is the filename var set earlier in the code.
Once you've finished editing the file and such, you can get the name and put it into that returned object and your client will receive that as object as the response which you can act upon.
Using Meteor.js, how can I serve an arbitrary HTTP response, eg. an image or PDF?
Example 1 - I need to generate PDF reports, which I cannot store in public/ or on a third-party server. Or, the report may be generated live in response to a HTTP GET.
Example 2 - If I have a url like:
/images/myimage.png
I would like to detect that request on the server, read the image from MongoDB, and serve it with the correct headers, so it is available to use with img tags, ie.
<img src="/images/myimage.png">
I do not want to store the images in the /public/ directory, so that I can have more control over exactly what is served and how it is permissioned.
Edit I was also able to get a basic example working using Iron Router.
ImageController = RouteController.extend({
run: function() {
var f = fs.readFileSync("/path/to/image.png");
var res = this.response;
res.writeHead(200, { "content-type": "image/png" });
res.write(f);
res.end();
}
});
Router.map(function() {
Router.route("images", {
path: "/images/image.png",
where: "server",
controller: ImageController // Note - cannot use string here - Iron Router has a dependency on window
});
});
You may write the response code as in any node app, using the middleware:
WebApp.connectHandlers.stack.splice (0, 0, {
route: '/path/to/the/file',
handle: function(req, res, next) {
res.writeHead(200, {
'Content-Type': ...ITEM TYPE... ,
});
res.write( ...ITEM DATA... );
res.end();
},
});
You can use filepicker. In filepicker the upload images is save in the bucket(cloud) and returns the url of that image. You can save the url in your mongo database. and when you want to use that image just use <img src="{{saveurl}}" > .
For more help see the documentation https://developers.inkfilepicker.com/docs/web/