I am using multer and cloudinary for image upload in node.js app. When I am in development mode, it works perfectly fine but after deploying to heroku, I am getting error in browser console as
the server responded with a status of 503 (Service Unavailable). When I check heroku logs it showed as
at=error code=H12 desc="Request timeout" method=POST path="/api/users/upload/avatar"
I have already added my environment variables in heroku. What could be a problem?
My config file
const {config,uploader } = require('cloudinary').v2
const cloudinaryConfig = (req,res,next) => {
config({
cloud_name: process.env.CLOUD_NAME,
api_key: process.env.API_KEY,
api_secret: process.env.API_SECRET,
});
next()
}
module.exports ={cloudinaryConfig,uploader}
Multer file
const DatauriParser = require('datauri/parser');
const parser = new DatauriParser();
const storage = multer.memoryStorage();
const multerUploads = multer({ storage,fileFilter:(req,file,cb)=>{
let ext = path.extname(file.originalname);
if (ext !== ".jpg" && ext !== ".jpeg" && ext !== ".png") {
cb(new Error("File type is not supported"), false);
return;
} cb(null,true)
} }).single('image');
const dataUri = req => parser.format(path.extname(req.file.originalname).toString(),req.file.buffer)
module.exports = {multerUploads,dataUri}
My upload controller
const uploadImage = async(req,res)=>{
const folder = req.path.split('/',3)[2]
if(!req.file){
throw new Error('Choose a picture to upload')
}
if(req.file){
const file = dataUri(req).content
const result = await uploader.upload(file,{
folder:`MobiHub/${folder}`,
width: 300,
height:300,
crop:'fill',
gravity: "faces"
})
const image = result.secure_url
res.json({image})
}
}
Setting the Cloudinary environment variables (e.g., process.env.CLOUD_NAME) on the Heroku config vars may help.
Or simply try to add the CLOUDINARY_URL environment variable (can be found on the Cloudinary dashboard), then you won't need to explicitly declare them on cloudinary.config().
Related
I'm wondering if anyone could help me. I have built a custom NextJS app on Heroku intended to be embedded on my Shopify storefront using Shopify's app proxy. It's mostly working, so it is showing my app when I go to the proxy url within my Shopify store however all of the CSS & JS filepaths have been changed to my Shopify URL rather than remaining as my Heroku app URL.
I have seen someone else have this exact same problem: https://community.shopify.com/c/Shopify-APIs-SDKs/App-Proxy-Sending-index-html-throws-404-on-the-JS-and-CSS-files/td-p/586595
However when I try to implement this solution it keeps breaking the app. I'm relatively new to Node.js/Next.js so while I'm still learning I largely depend on documentation to follow to set up or resolve this type of issue. However, I cannot find any tutorials or documentation specific to this issue.
Is anyone able to advise what I need to do within my server.js to make the '/_next/static/...' files remain to be pulled from my Heroku app URL rather than the Shopify URL when being viewed from the proxy URL? I have included the contents of my server.js file below.
require('isomorphic-fetch');
const dotenv = require('dotenv');
const Koa = require('koa');
const next = require('next');
const { default: createShopifyAuth } = require('#shopify/koa-shopify-auth');
const { verifyRequest } = require('#shopify/koa-shopify-auth');
const { default: Shopify, ApiVersion } = require('#shopify/shopify-api');
const Router = require('koa-router');
dotenv.config();
Shopify.Context.initialize({
API_KEY: process.env.SHOPIFY_API_KEY,
API_SECRET_KEY: process.env.SHOPIFY_API_SECRET,
SCOPES: process.env.SHOPIFY_API_SCOPES.split(","),
HOST_NAME: process.env.SHOPIFY_APP_URL.replace(/https:\/\//, ""),
API_VERSION: ApiVersion.October20,
IS_EMBEDDED_APP: true,
SESSION_STORAGE: new Shopify.Session.MemorySessionStorage(),
});
const port = parseInt(process.env.PORT, 10) || 3000;
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
const ACTIVE_SHOPIFY_SHOPS = {};
app.prepare().then(() => {
const server = new Koa();
const router = new Router();
server.keys = [Shopify.Context.API_SECRET_KEY];
server.use(
createShopifyAuth({
apiKey: process.env.SHOPIFY_API_KEY,
secret: process.env.SHOPIFY_API_SECRET,
scopes: ["read_products"],
afterAuth(ctx) {
const { shop, scope } = ctx.state.shopify;
ACTIVE_SHOPIFY_SHOPS[shop] = scope;
ctx.redirect(`/?shop=${shop}`);
},
}),
);
const handleRequest = async (ctx) => {
console.log('ctx.req.url', ctx.req.host);
await handle(ctx.req, ctx.res);
ctx.respond = false;
ctx.res.statusCode = 200;
};
router.get("/", async (ctx) => {
const shop = ctx.query.shop;
ACTIVE_SHOPIFY_SHOPS[shop] = ctx.query.shop;
if (ACTIVE_SHOPIFY_SHOPS[shop] === undefined) {
ctx.redirect(`/auth?shop=${shop}`);
} else {
await handleRequest(ctx);
}
});
router.get("/_next/*", handleRequest);
router.get("(.*)", verifyRequest(), handleRequest);
server.use(router.allowedMethods());
server.use(router.routes());
server.listen(port, () => {
console.log(`> Ready on http://localhost:${port}`);
});
});
Many thanks
A Proxy is simply a callback to your App, where as a response you provide either Liquid, or JSON. If you are trying to return links to assets such as CSS or JS, links are just strings. So can just send them as JSON key:value pairs.
A simple workaround is to use assetPrefix on next.config.js.
The assetPrefix will be prefixed for the /_next/ path automatically, so the assets are refered with absolute URL.
const isProd = process.env.NODE_ENV === 'production'
module.exports = {
assetPrefix: isProd ? 'https://yourappdomain.com' : '',
}
https://nextjs.org/docs/api-reference/next.config.js/cdn-support-with-asset-prefix
I have file upload functionality and I am using express-fileupload module in node.js to upload file to local folder. I am explaining my existing code below.
const fileUpload = require('express-fileupload');
app.use(fileUpload({
createParentPath: true
}));
const avatar = req.files.file;
avatar.mv('./uploads/' + avatar.name);
Here I can upload the file to my upload folder but here I need to add the timestamp with the file name and then upload so that I can differentiate if any new file is coming with same name.
This code may help you
const path = require('path');
let target_file = req.files.target_file;
var file_name = new Date().getTime() +'_'+target_file.name;
// target_file.mv(path, callback)
target_file.mv(path.join(__dirname, 'uploads', file_name), (err) => {
if (err) throw err;
res.send('File Uploaded');
})
The code(by Elangovan Selvaraj) before this comment is absolutely correct.
I tried with:
const pdfUpload = req.files.documents;
const fileName = new Date().getTime() + '_' + pdfUpload.name;
const pdfPath = path.join(
__dirname,
'../public/uploads/hotels/kyc/' + `${fileName}`
);
await pdfUpload.mv(pdfPath);
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
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.