In my MERN app, I am trying to access the images on the client-side that has been saved on the local database using multer.
The structure of my backend folder goes like this:
--- api
--- controllers
--- model
--- routes
--- config
--- db.js
--- appConfig.js
--- utils
--- uploads
--- multerMiddleware.js
--- app.js
The image uploading and storing to local DB works completely fine. When a new data is created, the data received by the client in API response contains the URL of the image uploaded so that it can be accessed again (like for displaying image thumbnail).
My code goes like:
App.js
const express = require("express");
const path = require('path');
const app = express();
const directory = path.join(__dirname, '/uploads');
app.use('/uploads', express.static(directory));
require("./config/db/db")();
require("./config/appRoutes/appRoutes")(app);
module.exports = app;
multerFile.js
const multer = require('multer');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, __dirname + '/uploads')
},
filename: function (req, file, cb) {
const fileName = file.originalname.toLowerCase().split(' ').join('-');
cb(null, fileName);
}
});
const upload = multer({
storage
});
module.exports = upload;
controller
exports.createService = async (req, res) => {
const service_name = req.body.main_name;
const url = req.protocol + '://' + req.get('host');
let service_pic;
if (req.file) {
service_pic = url + '/utils/uploads/' + req.file.filename;
}
try {
const service = new Services({
_id: new mongoose.Types.ObjectId(),
service_name,
service_pic
});
const new_service = await service.save();
res.status(201).json({ message: "New data created", result: new_service });
} catch (error) {
console.log(error);
res.status(500).json({ message: "Internal server error", error });
}
}
With the route, http://localhost:5000/services/all, I get the JSON data as:
{
createdAt: "2020-09-07T08:25:11.581Z"
service_name: "TEST"
service_pic: "http://localhost:5000/utils/uploads/testio-logo-rgb1.png"
updatedAt: "2020-09-07T08:25:11.581Z"
}
When I try to access http://localhost:5000/utils/uploads/testio-logo-rgb1.png, it always returns an error: "error":{"message":"Route Not found"}}. The images are gettings stored properly into the /uploads folder, but still not accessible on the client.
I am not sure what thing is going wrong. Any help to resolve this is appreciated.
Change '/uploads' to 'utils/uploads'
// app.js
const directory = path.join(__dirname, 'utils/uploads');
app.use('/uploads', express.static(directory));
This block of code means: you've set up a static-assets serving endpoint at /uploads. Everytime a request hits this endpoint, your server will look up to the folder /utils/uploads.
An example request would be: http://localhost:5000/uploads/testio-logo-rgb1.png
I believe your URL is incorrect,
http://localhost:5000/utils/uploads/testio-logo-rgb1.png
should be
http://localhost:5000/uploads/testio-logo-rgb1.png
Related
I’ve been having an issue with deploying my nodejs App on AWS ECS Fargate. Running the app locally on my device with nodemon or building the app and running the build file is successful and I can ping my routes using postman. The issue happens when I deploy this same exact code on AWS; using postman, to do a POST request, I get a 404 error. Please note, I'm running a Node:14 container.
For reference, my nodejs code is structured in a way where there’s a main route.js file containing all routes, then there are specific route files, for example listingRoute.js, contains all the sub-routes then there are controllers (.js files) containing all the logic where I export the function and tie it with the route in the listingRoute.js example.
Here's what my main Route.js file looks like:
const express = require('express');
const error = require('../Middleware/error');
const listingRoute = require('../Routes/listingRoute');
module.exports = function (app) {
//Middleware
app.use(express.json());
app.use(express.urlencoded({ extended: false , limit : '20mb' }));
app.use('/listing', listingRoute);
//The final middleware to be called in case of an unhandled error.
app.use(error);
process.on('uncaughtException', function(err) {
// Handle the error safely
console.log(err)
})
};
My listingRoute file
const express = require("express");
const route = express.Router();
const listingController = require("../Controllers/listingController");
require('dotenv').config();
route.post("/create", listingController.createListing)
route.post("/update", listingController.updateListing)
route.post("/read", listingController.getListing)
route.post("/delete", listingController.deleteListing)
...
...
...
...
...
route.post("/getMostPopular" , listingController.getMostPopular)
route.post("/getByCategory" , listingController.getByCategory)
route.post("/getAllTOS" , TOSController.getTOSByListing)
route.post("/getTOS" , TOSController.getTOSByID)
route.post("/updateTOS" , TOSController.updateTOS)
route.post("/deleteTOS" , TOSController.deleteTOS)
route.post("/createTOS" , TOSController.createTOS)
route.post("/getListingsByIDs" , listingController.getListingsByIDs)
route.post("/cacheImagesNewCDN" , listingController.cacheImagesNewCDN)
module.exports = route;
My listingController file
const listingModel = require('../Models/listingModel');
const moment = require('moment')
const axios = require('axios');
var fs = require('fs');
const createCsvWriter = require('csv-writer').createObjectCsvWriter;
var fs = require('fs');
//tested
const createListing =async (req, res) => {
try {
//some logic here
}
catch (err) {
console.log(err)
return res.status(500).json({ error: err.message });
}
}
const updateListing = async (req, res) => {
try {
//some logic here
}
catch (err) {
return res.status(500).json({ error: err.message });
}
}
module.exports = {
getListing,
updateListing,
deleteListing,
createListing,
listingwithViews,
advertisedListings,
filterListings,
pressedOnBookNow,
cacheImages,
recommendListings,
getCacheMetaData,
addIndoorAmenity,
missingFromFilter,
adjustCreativeStudios,
listingsToCSV,
getAllListing,
getDiscountedListings,
addRevenueToListings,
getMostPopular,
getByCategory,
getListingsByIDs,
cacheImagesNewCDN,
getOwnersPhones
}
All the routes starting from getMostPopular till the end of the list give an error 404 not found although I have done the same procedure to all of them. Any ideas why this is happening? If you feel this isn't enough information to help diagnose, let me know and i'd be happy to provide more details. You're help would be beyond appreciated, thanks!
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!
I made a webapp that lets you upload a file to the server using multer. It works like it should when the server is run locally, but when I deployed it on Heroku, it seems I ran into a 500 internal server error.
Has anyone dealt with this before?
What are the options?
Webapp is here: https://dupefinder.herokuapp.com/
Github repo is here: https://github.com/ExtDASH/herkodeploy
2018-09-19T19:38:48.310177+00:00 app[web.1]: POST /uploads 500 148 - 181.170 ms
2018-09-19T19:38:48.310830+00:00 app[web.1]: Error: ENOENT: no such file or directory, open 'uploads/csv1537385928295.csv'
2018-09-19T19:38:48.311255+00:00 heroku[router]: at=info method=POST path="/uploads" host=dupefinder.herokuapp.com request_id=ff1aaa34-f36c-49cf-bd4e-4a936fb48a2c fwd="24.52.32.175" dyno=web.1 connect=1ms service=188ms status=500 bytes=404 protocol=https
and here is the console error in the browser:
main.js:146 POST https://dupefinder.herokuapp.com/uploads 500 (Internal Server Error)
reader.onload # main.js:146
load (async)
readFile # main.js:131
invoker # vue.js:2029
Vue.$emit # vue.js:2538
click # VBtn.ts:108
invoker # vue.js:2029
fn._withTask.fn._withTask # vue.js:1828
I'm using an XMLHttpRequest POST request:
readFile: function(){
const input = document.querySelector('#myFile')
const reader = new FileReader()
reader.onload = function() {
let csvfile = new Blob([reader.result], { type: 'text/csv' })
app.uploadingFile = true
const form = new FormData()
let sendName = input.files[0].name.split(/\W+/g)
form.append('Ncsv', csvfile, `${sendName[0]}.csv`)
const xhr = new XMLHttpRequest()
xhr.open('POST', '/uploads', true)
xhr.onreadystatechange = function() {
if(this.readyState == XMLHttpRequest.DONE && this.status == 200) {
form.delete('Ncsv')
}
}
xhr.send(form)
}
reader.readAsText(input.files[0])
which goes to an app.post route in my server.js file:
const express = require('express')
const connect = require('connect')
const morgan = require('morgan')
const bodyParser = require('body-parser')
const mongoose = require('mongoose')
const fs = require('fs-extra')
const multer = require('multer')
const getRouter = require('./routes/ourNums')
const nFs = require('./fileSchema.js')
const namesRouter = require('./routes/namesRouter.js')
const computeRouter = require('./routes/computeRouter.js')
// const uploadRouter = require('./routes/uploadRouter') unused for now
const filesRouter = require('./routes/filesRouter')
const path = require('path')
const app = express()
var storage = multer.diskStorage({
destination: function (req, file, cb) {
fs.ensureFile(file)
.then(() => {
console.log('done')
}
cb(null, __dirname+'/uploads')
},
filename: function (req, file, cb) {
var newName = file.originalname.split(/\W+/g)
var fullName = `${newName[0]}${Date.now()}.csv`
cb(null, fullName)
},
})
var upload = multer({ storage: storage })
app.use(express.json({limit: '50mb'}))
app.use(bodyParser.urlencoded({extended: false}))
app.use(bodyParser.json())
app.use(bodyParser.json({ limit: '50mb' }))
app.use(morgan('tiny')) //watching for changes
// app.use(express.static(`${__dirname}/client/index.html`))
app.post('/uploads', upload.single('Ncsv'), function (req, res, next) {
var fileName = req.file.filename
nFs.create({
name: fileName
})
.then(data => res.status(200).send())
.catch(e => {
req.error = e
console.log(e)
next()
})
})
before, I didn't use fs for anything (and as you can see here, fs.ensureFile isn't doing anything to fix the 500 error), I just included it to begin with so I could play around with it. Running the server locally, this works. I click my upload button on my client and it sends whatever file i selected as a blob, runs it through multer, and creates the file in a /server/uploads/ directory
Edit: I just tried using multer.memoryStorage() and got the same 500 internal server error.
I'm not sure why your uploads aren't being saved; you should be able to save them temporarily.
But this won't work long-term. Heroku's filesystem is ephemeral: any changes you make will be lost the next time your dyno restarts, which happens frequently (at least once per day).
Heroku recommends storing uploads on something like Amazon S3. Here's a guide for doing it specifically with Node.js.
Once you've stored your files on S3 you should be able to retrieve them using an appropriate library or possibly over HTTP, depending on how you've configured your bucket.
If you still wish to use multer, check out multer-s3.
I am a beginner at ionic framework developing.
This is flow of my ionic app.
- Select image from folders and press "upload a picture" button.
- I used ionic-native-file transfer for uploading to Nodejs express server.
This is my code.
//ionic page
https://www.dropbox.com/s/k1nittp0p8t4ay3/item-create.rar?dl=0
//Node js source
https://www.dropbox.com/sh/0zd9ydk0uhhz5g7/AABIg9S7hV6XiIzrMTj8FKA2a?dl=0
Main Point:
app.post('/upload', function(req,res)) , uploadImage()
//ionic3-item.js
uploadImage() //When press upload button
{
const fileTransfer:FileTransferObject = this.transfer.create();
let option: FileUploadOptions = {
fileKey:'file',
fileName:'name.jpg',
mimeType:'image/jpeg'
};
fileTransfer.upload(this.fileurl, encodeURI("http://192.168.1.249:8080/upload"),option);
}
}
//This Node js server code.
//route/ index.js
module.exports = function(app, Article)
{
//Uploaded Article------------------This part -------------------------
app.post('/upload', function(req,res){
console.log(req.files);
});
}
But req.files is undefined.
I wonder how I can treat the uploaded files from ionic app.
Please help.
Thanks.
This is client source.
var name = "upload";
let option: FileUploadOptions = {
fileKey:'file',
mimeType:'audio/3gp',
httpMethod:'POST',
fileName:'user_step4#'+name
};
this.loader = this.loadingCtrl.create({
content:'登录中...',
});
this.loader.present();
const fileTransfer:FileTransferObject = this.transfer.create();
console.log('filename'+this.curfilename);
fileTransfer.upload(this.file.externalRootDirectory+this.curfilename, encodeURI(localStorage.getItem('GlobalIP')+"/upload"),option).then((result)=>
{
console.log('success');
}).catch(error=>{
this.loader.dismiss();
console.log('uploaderror');
console.log(error.message);
});
}
This is server code
var multer = require('multer');
var storage = multer.diskStorage({
destination:function(req, file, cb)
{
console.log('uploadpath:'+file.originalname);
var pathname = file.originalname.split('#');
console.log(file.originalname);
var path = pathname[0].replace('_','/');
console.log(path);
cb(null,'public/resources/'+path);
},filename:function(req,file,cb)
{
var pathname = file.originalname.split('#');
var filename = pathname[1];
console.log(filename);
if(filename!=undefined)
cb(null, filename);
}
});
//For multipart/form-data Uploading
var upload = multer({storage:storage});
app.post('/upload',upload.single('file'), function(req,res,next)
{
console.log("uploaded");
res.json({result:1});
});
Thanks for reading.
does anyone know how to use tinyPNG's API with multer? The docs seem deceptively simple:
var source = tinify.fromFile("unoptimized.jpg");
source.toFile("optimized.jpg");
though there's no clear indication of where this is meant to go, especially in something as convoluted as this:
var storage = multer.diskStorage(
{
destination: function (req, file, callback) {
callback(null, './uploads');
},
filename: function (req, file, callback) {
//use date to guarantee name uniqueness
callback(null, file.originalname + '-' + Date.now());
}
}
);
//.any() allows multiple file uploads
var upload = multer({ storage : storage}).any()
app.post('/api/photo', function(req,res){
upload(req,res,function(err) {
if(err) {
return res.end("Error uploading file.");
}
res.end("File is uploaded");
});
});
Where am I meant to "intercept" the file uploaded by multer so that I can compress it with tinyPNG?
Thanks in advance for the help!
Use following basic sample that changes uploaded photo/gallery files:
// Import express and multer.
var express = require('express');
var multer = require('multer');
// Setup upload.
var upload = multer({ dest: 'uploads/' });
var multipleFiles = upload.fields([{ name: 'photo', maxCount: 1 },
{ name: 'gallery', maxCount: 8 }]);
// Setup tinify.
var tinify = require("tinify");
tinify.key = "YOUR_API_KEY";
// Get request handler for '/' path.
var app = express();
app.get('/', function (req, res) {
res.setHeader("Content-Type", "text/html");
res.end(
"<form action='/api/photo' method='post' enctype='multipart/form-data'>" +
"<input type='file' name='photo' />" +
"<input type='file' name='gallery' multiple/>" +
"<input type='submit' />" +
"</form>"
);
});
// Upload file handler with '/api/photo' path.
app.post('/api/photo', multipleFiles, function (req, res) {
req.files['gallery'].forEach(function(file) {
// Your logic with tinify here.
var source = tinify.fromFile(file.path);
source.toFile(file.path + "_optimized.jpg");
});
res.end("UPLOAD COMPLETED!");
});
Feel free to change express middleware how you need it, just make sure you use upload.fields and authenticate using tinify.key = "YOUR_API_KEY";
https://github.com/expressjs/multer
https://tinypng.com/developers/reference/nodejs#compressing-images
I recently worked out a similar problem for myself using the tinify package and found the docs to be somewhat lacking.
I have a Vue front end collecting file uploads from the user using vue2dropzone. These are sent to a node / Express back end.
I have a need to compress the file and upload it to an S3 instance without storing on disk. That means using multer memory storage.
As a result there won’t be an ability to use tinify.fromFile() as there is no file stored locally.
In my images middleware:
Const multer = require(“multer”);
const tinify = require("tinify");
tinify.key = "your_key";
exports.singleFile = multer({ storage: multer.memoryStorage() }).fields([{ name: "file", maxCount: 1 }]);
exports.uploadCompImage = async (req, res, next) => {
try {
const fileName = `${req.params.name}${path.extname(req.files.file[0].originalname)}`;
const source = tinify.fromBuffer(req.files.file[0].buffer);
source.store({
service: "s3",
aws_access_key_id: "your_id",
aws_secret_access_key: "your_key
region: "your_region",
headers: {
"Cache-Control": "public"
},
path: `your_bucket/your_folder/${fileName}`
});
return res.status(200).send(`path_to_file/${fileName}`)
} catch (err) {
console.log(err);
next(err);
}
}
Then in my routes file:
Const images = require(“../middleware/images”);
// skipped several lines for brevity
productRouter
.route("/images/:name")
.post(images.singleFile, images.uploadCompImage)
This process creates a multer singleFile upload to memoryStorage, making the file available at req.files.file[0] (req.files[“file”] because I specified “file” as the name in multer fields, loop through this array if uploading multiple).
After setting that up I get the file name, set the source by using tinify to read from req.files.file[0].buffer as a buffer.
Then I set the source to my s3 instance and send back a public link to the file.
Hopefully this answer helps you. I could definitely see altering the process to change where the file goes or even write it to disk by altering the multer options.