I am creating file upload functionality on Cloud Storage. Here's the backend code:
const Storage = require('#google-cloud/storage')
const storage = Storage({
projectId: 'a-1485'
})
const bucket = storage.bucket('a-1485.appspot.com')
function getPublicUrl (filename) {
return 'https://storage.googleapis.com/a-1485.appspot.com/${filename}'
}
function sendUploadToGCS (req, res, next) {
if (!req.file) {
return next()
}
const gcsname = Date.now() + req.file.originalname
console.log(gcsname)
const file = bucket.file(gcsname)
const stream = file.createWriteStream({
metadata: {
contentType: req.file.mimetype
}
})
stream.on('error', (err) => {
req.file.cloudStorageError = err
next(err)
})
stream.on('finish', () => {
req.file.cloudStorageObject = gcsname
req.file.cloudStoragePublicUrl = getPublicUrl(gcsname)
next()
})
stream.end(req.file.buffer);
}
module.exports = {
getPublicUrl,
sendUploadToGCS
}
In my app.js file:
app.post('/upload-image', multer.single('image'), images.sendUploadToGCS, (req, res, next) => {
let data = req.body
console.log(data)
if (req.file && req.file.cloudStoragePublicUrl) {
data.imageUrl = req.file.cloudStoragePublicUrl
}
getModel().create(data, (err, savedData) => {
if (err) {
next(err)
return
}
res.redirect(`${req.baseUrl}/${savedData.id}`);
})
}
)
However, when I upload an image I get an error thrown saying 'getModel()' is not defined. But as you can see above it is defined. What is the problem?
Related
Like I said on the title, I'm facing that error when I'm trying to upload a file from the client-side. Here are the code blocks for more context:
Route:
const express = require("express");
const { upload } = require("../middlewares/uploadMiddleware");
const { registerResearchSheet } = require("../controllers/researchSheetControllers");
const router = express.Router();
router.route("/").post(upload.single("file"), registerResearchSheet);
Middleware:
const multer = require("multer");
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "backend/uploads/");
},
filename: function (req, file, cb) {
cb(
null,
new Date().toISOString().replace(/:/g, "-") + "-" + file.originalname
);
},
});
const fileFilter = (req, file, cb) => {
if (file.mimetype === "application/pdf") {
cb(null, true);
} else {
cb(null, false);
}
};
const upload = multer({ storage: storage, fileFilter: fileFilter });
module.exports = { upload };
Controller:
const asyncHandler = require("express-async-handler");
const ResearchSheet = require("../models/researchSheetModel");
const registerResearchSheet = asyncHandler(async (req, res) => {
const {
title,
director,
coordinator,
students,
reviewers,
proposalType,
investigationType,
status,
} = req.body;
if (!req.file) {
res.status(400);
throw new Error("The file hasn't be uploaded correctly"); **//This is the triggering error**
}
const file = req.file.path;
const researchSheetExists = await ResearchSheet.findOne({ title, students });
if (researchSheetExists) {
res.status(400);
throw new Error("Title or students can't be repeated");
}
const researchSheet = await ResearchSheet.create({
title,
file, // file: buffer,
director,
coordinator,
students,
reviewers,
proposalType,
investigationType,
status,
});
if (researchSheet) {
res.status(201).json({
_id: researchSheet._id,
title: researchSheet.title,
file: researchSheet.file,
director: researchSheet.director,
coordinator: researchSheet.coordinator,
students: researchSheet.students,
reviewers: researchSheet.reviewers,
proposalType: researchSheet.proposalType,
investigationType: researchSheet.investigationType,
status: researchSheet.status,
});
} else {
res.status(400);
throw new Error("An error has ocurred");
}
});
And finally the frontend part:
const [file, setFile] = useState(null);
//code..
const singleFileUpload = (e) => {
setFile(e.target.files[0]);
};
const submitHandler = async (e) => {
e.preventDefault();
const formData = new FormData();
formData.append("title", title);
formData.append("file", file);
formData.append("director", director);
formData.append("coordinator", coordinator);
formData.append("students", students);
formData.append("proposalType", proposalType);
formData.append("investigationType", investigationType);
console.log(file); //This log is correctly showing the File and its properties
if (
!title ||
!coordinator ||
!students ||
!proposalType ||
!investigationType
) {
setMessage("You must fill all the fields");
} else {
dispatch(registerResearchSheetAction(formData));
// history.push("/listresearchsheets");
}
};
//more code...
<Form.Control
className="mb-3"
type="file"
accept=".pdf"
onChange={(e) => singleFileUpload(e)}
/>
<Button type="submit">Register</Button>
Action:
export const registerResearchSheetAction =
(
title,
file,
director,
coordinator,
students,
reviewers,
proposalType,
investigationType,
status
) =>
async (dispatch) => {
try {
dispatch({
type: RESEARCHSHEET_REGISTER_REQUEST,
});
const formData = new FormData();
formData.append("file", file);
formData.append("title", title);
formData.append("director", director);
formData.append("coordinator", coordinator);
formData.append("students", students);
formData.append("reviewers", reviewers);
formData.append("proposalType", proposalType);
formData.append("investigationType", investigationType);
formData.append("status", status);
const config = {
headers: {
"Content-Type": "multipart/form-data",
},
};
const { data } = await axios.post(
"/api/researchsheets",
formData,
config
);
dispatch({ type: RESEARCHSHEET_REGISTER_SUCCESS, payload: data });
} catch (error) {
dispatch({
type: RESEARCHSHEET_REGISTER_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
});
}
};
I already tested the functionality on the server-side using Postman and it is uploading, more than the file, the route/path of it successfully on the database. That's why I'm thinking that the problem must be on the client-side. I really don't know what I'm missing here, so that's why I came for a little of help, I hope I was clear on my explanaiton.
Thank you in advance!
I have the following middleware in a Node.js REST API
const Authorized = (req, res, next) => {
if (!req.headers["authorization"]) {
res.status(401).send("Unauthorized");
} else {
jwt.verify(req.headers["authorization"], PRIVATE_KEY, (err, decoded) => {
if (err) {
res.status(403).send("Forbidden");
} else {
req.businessId = decoded.businessId;
req.roleId = decoded.roleId;
next();
}
});
}
};
As you can see I'm adding to variables to the request object
In the mockup of my tests I'm trying to do this:
sandbox = sinon.createSandbox();
sandbox.stub(Authorized, "Authorized")
.callsFake(async (req, res, next) => {
req.businessId = businessAux.id;
return next();
});
But this doesn't work and my actual function to be tested needs this variable:
listPermissionAndRolesByPk() {
this.app.get(`${config.API_PATH}/permissionrolebypk`, Authorized.Authorized, async (req, res) => {
const id = req.query.id;
const businessId = req.businessId;
if (!id) return res.status(400).send(messages[23].message.replace("${object}", "Permission Id"));
try {
const permission = await PermissionDAO.getPermissionAndRolesByPk(id, businessId ? businessId : 0);
if (permission) {
return res.status(200).json(permission);
} else {
return res.status(404).send(messages[8].message.replace("${object}", "Permission"));
}
} catch (error) {
return res.status(error.httpCode).send(error.message);
}
});
}
Any help will be appreciated.
I am using multer to upload a file to my backend. Here is the route:
// #route POST api/users/resume
// #desc Upload user resume
// #access Private
router.post("/resume", auth, async (req, res) => {
try {
let resumeBackupName = null;
let resumeMatrix = {
"application/vnd.ms-excel": "xls",
"application/msword": "doc",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document":
"docx",
"application/pdf": "pdf"
};
let user = await User.findById({ _id: req.user.id.toString() });
if (
user.resume &&
user.resume.extension &&
user.resume.extension.length > 0
) {
resumeBackupName =
req.user.id.toString() +
"-backup-" +
user.resume.extension.length.toString() +
"." +
user.resume.extension[user.resume.extension.length - 1];
user.resume.backup.push(resumeBackupName);
}
let date = new Date();
let resumeMimeType = null;
let resumeExtension = null;
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "public");
},
filename: (req, file, cb) => {
cb(
null,
req.user.id.toString() +
"-" +
date.getTime() +
"-" +
file.originalname
);
}
});
const upload = multer({ storage: storage }).single("file");
upload(req, res, err => {
resumeMimeType = req.file.mimetype;
resumeExtension = resumeMatrix[resumeMimeType];
user.resume.mimeType = resumeMimeType;
user.resume.extension.push(resumeExtension);
if (err instanceof multer.MulterError) {
return res.status(500).json(err);
} else if (err) {
return res.status(500).json(err);
}
});
await user.save();
return res.json({ msg: "Success!" });
} catch (err) {
console.log(err);
res.status(500).send("Server error");
}
});
The problem is within the upload function. Console logging user after the user.resume.mimeType = resumeMimeType; and user.resume.extension.push(resumeExtension); lines properly displays the values, but when await user.save() is hit, it doesn't save anything. Is it a scoping issue? I'm getting a warning variables declared but never used for let resumeMimeType = null; and let resumeExtension = null;, but they are used in the upload function. Not sure what I'm doing wrong.
I am trying to create a nodejs API separated by controllers and routes. I am trying to findandupdate in multiple collections and then put them in multiple promises to return a single response but i get just a null what am i doing wrong below ?
controller.js
var x = (req, res, next, userID, product) => {
let query = {
uid: userID
}
let update = {
$push: {
product: product,
}
}
let options = {
safe: true,
new: true,
upsert: true
}
Model.findOneAndUpdate(query, update, options).exec()
.then(result => {
return true
})
.catch(err => {
console.log(err);
res.status(500).json({ error: err });
})
};
module.exports = x;
Route.js
const controller = require('./user-product')
router.post('/api', function (req, res, next) {
var p1 = controller(req, res, next, userID, product)
var allDone = Promise.all([p1])
allDone
.then(function (e) {
res.send(e) //this is null
})
.catch(function (e) {
console.log(e);
})
});
You are not returning promise from the controller function. You are just returning the result and the error
So, Instead you should return the promise inside the controller function
Route.js
const controller = require('./user-product')
router.post('/api', function (req, res, next) {
var p1 = controller(req, res, next, userID, product)
var allDone = Promise.all([p1])
allDone.then(function (e) {
res.send(e)
})
.catch(function (e) {
console.log(e);
})
})
controller.js
var x = (req, res, next, userID, product) => {
let query = { uid: userID }
let update = { $push: { product: product }}
let options = {
safe: true,
new: true,
upsert: true
}
return Model.findOneAndUpdate(query, update, options).exec()
}
module.exports = x;
And probably easier with the async await syntax
Route.js
const controller = require('./user-product')
router.post('/api', async(req, res, next) => {
try {
const p1 = await controller(req, res, next, userID, product)
console.log(p1)
} catch (err) {
console.log(err)
}
})
controller.js
var x = async(req, res, next, userID, product) => {
let query = { uid: userID }
let update = { $push: { product: product }}
let options = {
safe: true,
new: true,
upsert: true
}
return Model.findOneAndUpdate(query, update, options).exec()
}
module.exports = x;
When i send DELETE request i got message error
ReferenceError: id is not defined
at Object.removeOne (...\services\user.js:16:38
I have no idea what id about in \services\user.js, and why it is not defined...
./generalRepository.js
function Repository() {}
Repository.prototype.findAndRemoveById = findAndRemoveById;
function findAndRemoveById(id, callback) {
var model = this.model;
var query = model.deleteOne({
_id: id
});
query.exec(callback);
}
module.exports = Repository;
.routers/user.js
const router = require("express").Router();
const userService = require("../../services/user");
router.delete("/:id", (req, res, next) => {
userService.removeOne(String(req.params.id), (err, data) => {
if (!err) {
res.send('success delete query');
} else {
console.log("wrong delete query");
res.status(400);
res.end();
}
});
});
module.exports = router;
.services/user.js
const UserRepository = require("../repositories/UserRepository");
module.exports = {
removeOne: () => {
UserRepository.findAndRemoveById(id, (err, data) => {
callback(err, data);
});
}
};
You need to update removeOne function to following by expecting arguments (id and callback) to be passed when it is being called.
const UserRepository = require("../repositories/UserRepository");
module.exports = {
removeOne: (id, callback) => {
UserRepository.findAndRemoveById(id, (err, data) => {
callback(err, data);
});
}
};
Maybe you should be like this:
module.exports = {
removeOne: (id) => {
UserRepository.findAndRemoveById(id, (err, data) => {
callback(err, data);
});
}
};