I'm creating an api using Sails JS v1.0.0
I have an action to upload an image to the server and it's working great but the problem I'm having is that I want to save the image URL to the user uploaded the image. It's the user profile image.
The code seems to work fine but I get an error in the terminal after uploading the image. I guess it has something with the callbacks.
Here is my controller:
let fs = require('fs');
module.exports = {
upload : async function(req, res) {
req.file('image').upload({ dirname : process.cwd() + '/assets/images/profile' }, function(err, uploadedImage) {
if (err) return res.negotiate(err);
let filename = uploadedImage[0].fd.substring(uploadedImage[0].fd.lastIndexOf('/')+1);
let uploadLocation = process.cwd() +'/assets/images/uploads/' + filename;
let tempLocation = process.cwd() + '/.tmp/public/images/uploads/' + filename;
fs.createReadStream(uploadLocation).pipe(fs.createWriteStream(tempLocation));
res.json({ files : uploadedImage[0].fd.split('assets/')[1] })
})
}
};
About the read stream to the .tmp folder, I wrote it to make the image available the moment it gets uploaded.
I tried to query for the user right before the
res.json({ files : uploadedImage[0].fd.split('assets/')[1] })
line, but it gives me an error in the terminal.
What's the best way to implement this code?
User.update({ id : req.body.id }).set({ image : uploadedImage[0].fd.split('images/')[1] });
You are uploading images to '/assets/images/profile' and trying to fetch it from '/assets/images/uploads/'. Also wrong path in tempLocation variable too. Change your code to following and it will hopefully start working
upload : async function(req, res) {
req.file('image').upload({ dirname : process.cwd() + '/assets/images/profile' },
async function(err, uploadedImage) {
if (err) return res.negotiate(err);
let filename = uploadedImage[0].fd.substring(uploadedImage[0].fd.lastIndexOf('/')+1);
let uploadLocation = process.cwd() +'/assets/images/profile/' + filename;
let tempLocation = process.cwd() + '/.tmp/public/images/profile/' + filename;
fs.createReadStream(uploadLocation).pipe(fs.createWriteStream(tempLocation));
await User.update({ id : req.body.id }).set({ image : uploadedImage[0].fd.split('images/')[1] });
res.json({ files : uploadedImage[0].fd.split('assets/')[1] })
})
},
Related
Seems like many people have ran into similar kind of problem but googling hasn't helped me so far. I'm trying to serve the file to the user and automatically prompt download on client side, but all I'm getting is "The image cannot be displayed because it contains errors.
Here is my client side code:
function downloadFile(passcode){
console.log(passcode);
const payload = {"password" : passcode.toLowerCase()}
axios.post(downloadUrl, payload, {
headers : {'Content-Type' : 'application/json'}
})
.then((res) => {
console.log(res.data)
window.open(downloadUrl + '/' + res.data)
})
}
So the user types in a passcode, and clicks on the download button and should get the appropriate file. New tab opens but file doesn't stard downloading.
Here is my server side:
const getFilePath = async (req, res) => {
const passcode = req.body.password
try {
fs.readdir(path.join(homeDir + '/uploads/' + passcode), 'utf-8',(err, files) => {
files.forEach((file) => {
const filename = passcode + '/' + file
try {
res.send(filename)
res.end()
} catch (error) {
console.log(error);
}
})
})
} catch (error) {
console.log(error);
}
}
const fileDownload = async (req, res) => {
const {dir: directory, file: fileName} = req.params
const filePath = path.join(homeDir + '/uploads/' + directory + '/' + fileName)
fs.access(filePath, fs.constants.F_OK, err => {
//check that we can access the file
console.log(`${filePath} ${err ? "does not exist" : "exists"}`);
});
res.download(filePath)
res.end()
}
I even check the file with fs.access and it returns true (it prints {filepath} exists), but the file is not served at all.
Any kind of help is much appreciated!
EDIT:
Decided to work on my front end a bit to cool off, came back and immidiatelly noticed res.end() just below send file, which ends the transmission. Removed it and it works like a charm!
I am trying to get a file from html form and store it in another folder. It's basically cloud function, and I am new to both node.js and firebase so don't know what I am doing wrong. What I manage to do is:
const fileMiddleware = require('express-multipart-file-parser');
app.post("/sendMail", (req, res) => {
const {
fieldname,
filename,
encoding,
mimetype,
buffer,
} = req.files[0];
console.log(req.files[0].originalname);
var fs = require('fs')
var oldPath = req.files[0].originalname;
var newPath = '/functions/'+oldPath;
fs.rename(oldPath, newPath, function (err) {
if (err) throw err
console.log('Successfully renamed - AKA moved!')
});
});
Whenever I try to move file, I got path issues. The error is as follows:
[Error: ENOENT: no such file or directory, rename 'C:\Users\Maisum Abbas\now\functions\sendMail.txt'
> 'C:\functions\sendMail.txt'] {
> errno: -4058,
> code: 'ENOENT',
> syscall: 'rename',
> path: 'C:\\Users\\Maisum Abbas\\now\\functions\\sendMail.txt',
> dest: 'C:\\functions\\sendMail.txt'
> }
Also, this is the path where I want to actually move the file but oldpath is already setup like this.
C:\Users\Maisum Abbas\now\functions\sendMail.txt
Since I needed to attach a file with email, it was causing path issues. I tried it with multer and it works. What I did:
//call libraries here
var storage = multer.diskStorage({
destination: function (req, file, callback) {
callback(null, 'resume/');
},
filename: function (req, file, callback) {
callback(null, file.fieldname + '-' + Date.now());
}
});
var upload = multer({ storage : storage}).single('filetoupload');
app.post("/careerMail", (req, res) => {
const { name } = req.body;
const { email } = req.body;
const { phone } = req.body;
upload(req,res,function(err) {
if(err) {
return res.end("Error uploading file.");
}
});
const dest = 'mymail';
const mailOptions = {
from: email, // Something like: Jane Doe <janedoe#gmail.com>
to: dest,
subject: 'Candidate Application', // email subject
html: `<div>
<strong>From:</strong> ` +
name +
`<br /><br />
<strong>Email:</strong> ` +
email +
`<br /><br />
<strong>Phone:</strong> ` +
phone +
`<br /><br />
</div>
`,// email content in HTML
attachments: [
{
filename: req.files[0].originalname,
content: req.files[0].buffer.toString("base64"),
encoding: "base64"
}
]
and rest of the code...
I suggest rethinking this approach altogether. You won't be able to move files around in a deployed function. The nodejs runtime filesystem doesn't allow any files to be written anywhere in the filesystem, except for os.tmpdir() (which is /tmp on Linux).
If you need to write a file temporarily, you should definitely only use that tmp space. Be aware that files written there occupy memory and should be deleted before the function terminates, or you could leak memory.
You can read files that you deployed with your code, but you should do that through relative paths.
I ran into same problem while moving file. I sort this problem by using a function to get the application root folder and then concatenate rest of the location.
//place this file on application root.
//import where you need to get the root path.
const path = require('path');
module.exports = (function(){
return path.dirname(require.main.filename || process.mainModule.filename);
})();
//taking your case move location.
const rootPath = //require the above module.
const newPath = rootPath + /functions/' +oldPath;
fs.rename(oldPath, newPath, function (err) {
if (err) throw err
console.log('Successfully renamed - AKA moved!')
});
I am using express-fileupload to upload the images. The images are saved in my local directory. I want to insert the name of the file to the mongodb if possible. Finally I want the image to be displayed in my frontend.
function insertRecord(req,res){
if(req.files){
const file=req.files.filename
filename=file.name
file.mv("./upload"+filename,function(err){
if(err)
console.log(err)
})
}
const user=new User()
user.name=req.body.name
user.address=req.body.address
user.email=req.body.email
user.mobile=req.body.mobile
user.filename=req.body.filename
user.save((err,docs)=>{
if(!err){
res.redirect('/user/list')
}
else {
if (err.name == 'ValidationError') {
handleValidationError(err, req.body);
res.render("./users/addOrEdit", {
viewTitle: "Insert User",
user: req.body
});
}
else
console.log('Error during record insertion : ' + err);
}
});
}
I am not sure whether the way to insert the name of the file to the mongodb is correct or not. Anyway, that is optional but I am not understanding how can I display the uploaded images which are present in the local directory.
I tried to save the image as base64 but the record is not saved to the database now.
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/')
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
})
var upload = multer({ storage: storage })
router.post('/',upload.single('myImage'),function(req,res){
if (req.body._id == '')
insertRecord(req, res);
else
updateRecord(req, res);
})
function insertRecord(req,res){
var img = fs.readFileSync(req.file.path);
var encode_image = img.toString('base64');
var finalImg = {
contentType: req.file.mimetype,
image: new Buffer(encode_image, 'base64')
};
const user=new User()
user.name=req.body.name
user.address=req.body.address
user.email=req.body.email
user.mobile=req.body.mobile
user.save(finalImg,(err,docs)=>{
if(!err){
res.redirect('/user/list')
}
else {
if (err.name == 'ValidationError') {
handleValidationError(err, req.body);
res.render("./users/addOrEdit", {
viewTitle: "Insert User",
user: req.body
});
}
else
console.log('Error during record insertion : ' + err);
}
});
}
Edit: I think there is a problem in the code: it should be `'./upload/'+filename' not without the second slash.
In order to show the images, you have to open a static route in Express. Example: app.use('/images', express.static(PATH)). Then you can, in the frontend, call it as <img src="URL/images/FILENAME" />
From your code, it is not possible to understand what kind of data you are sending to the server. As far as I understand, you're trying mv the string filename. In order to transfer files (such as images), you should have form-data instead of JSON data or you should encode the image file into Base64 to transfer it as text (not the filename, the whole file).
Check Multer out for this kind of job. It is described well in the README.md. Apart from that, until you submit the form, the image won't be available in the front-end. If you want to preview the image before uploading it's a separate process which you can learn more in here.
Hello everyone I'm trying to create an app with a backend where the user will be able to upload a profile pic. I'm using nodejs for the back with the framework Expressjs.
However, when I try to upload an image to my server from my react-native app nothing gets saved into my images folder.
I'm on iOS right now, I don't know if it changes anything.
When I tried to do the same but without react-native directly sending POST request using Postman I was able to do so.
I don't really know why I thought it would be extension name or something but it does not change anything.
Here is my backend code first :
I'm using Multer for the upload
const storage = multer.diskStorage({
destination : function(req, file, cb ){
cb(null,'./images/');
},
filename : function(req,file,cb){
cb(null, Date.now() + file.originalname);
}
})
const upload = multer({ storage : storage,
//filer only valide file == image
fileFilter: function(req,file,cb){
let ext = path.extname(file.originalname);
if(ext != '.png' && ext != '.jpg' && ext !='jpeg'){
return cb('Only images are allowed ')
}
cb(null, true)
},
limits:{fieldSize: 25*1024*1024}
})
// route for photo TEST
router.post('/upload',upload.single('photo'), (req,res)=> {
console.log('file',req.files);
//console.log('body', req.body);
res.status(200).json({
message:'success!',
});
});
And now this is what I do on my react-native app
const createFormData = (photo, body) =>{
const data = new FormData();
data.append('photo', {
name:photo.fileName,
type:photo.type,
uri :
Platform.OS === 'android' ? photo.uri : photo.uri.replace('file://', ''), // to be sure it works with android
});
Object.keys(body).forEach(key => {
data.append(key, body[key]);
});
return data;
handleUploadPhoto = () =>{
fetch('http://localhost:5050/api/new/upload',{
method: 'POST',
body: createFormData(this.state.dataPhoto,{userId : 'test'}),
})
.then(response => response.json())
.then(response => {
console.log('upload succes', response);
alert('Photo updated succesfully!')
})
.catch(error => {
console.log('upload failed', error);
alert('Upload is not possible right now!');
})
};
I do get the alert("Photo updated succesfully on my screen" so I guess it works on the front but not on the back...
I load the image using react-native-image-picker
I don't get any error message but no files are saved into my images folder.
Thanks in advance for any help
Are you not seeing anythign related to console.log('file', req.files) which you printed ?
Verify when you upload , you are getting the file + the uploaded file related ojbects in the node console.
Enable the console body log and also view it console.log('body', req.body)
I have this cloud function that I wrote to upload file to google cloud storage:
const gcs = require('#google-cloud/storage')({keyFilename:'2fe4e3d2bfdc.json'});
var filePath = file.path + "/" + file.name;
return bucket.upload(filePath, {
destination: file.name
}).catch(reason => {
console.error(reason);
});
I used formidable to parse the uploaded file and I tried to log the properties of the uploaded file and it seems fine; it is uploaded to a temp dir '/tmp/upload_2866bbe4fdcc5beb30c06ae6c3f6b1aa/ but when I try to upload the file to the gcs am getting this error:
{ Error: EACCES: permission denied, stat '/tmp/upload_2866bbe4fdcc5beb30c06ae6c3f6b1aa/thumb_ttttttt.jpg'
at Error (native)
errno: -13,
code: 'EACCES',
syscall: 'stat',
path: '/tmp/upload_2866bbe4fdcc5beb30c06ae6c3f6b1aa/thumb_ttttttt.jpg' }
I am using this html form to upload the file:
<!DOCTYPE html>
<html>
<body>
<form action="https://us-central1-appname.cloudfunctions.net/uploadFile" method="post" enctype="multipart/form-data">
Select image to upload:
<input type="file" name="fileToUpload" id="fileToUpload">
<input type="submit" value="Upload Image" name="submit">
</form>
</body>
</html>
I got a solution from the Firebase Support Team
So first thing:
var filePath = file.path + "/" + file.name;
we dont need the file.name since the file.path is full path of the file (including the file name).
So changed it to this instead:
var filePath = file.path;
Second, the function terminates before the asynchronous work in 'form.parse(...)' is completed. That means the actual file upload might still be in progress while the function execution has ended.
The fix for that is to wrap the form.parse(...) in a promise:
exports.uploadFile = functions.https.onRequest((req, res) => {
var form = new formidable.IncomingForm();
return new Promise((resolve, reject) => {
form.parse(req, function(err, fields, files) {
var file = files.fileToUpload;
if(!file){
reject("no file to upload, please choose a file.");
return;
}
console.info("about to upload file as a json: " + file.type);
var filePath = file.path;
console.log('File path: ' + filePath);
var bucket = gcs.bucket('bucket-name');
return bucket.upload(filePath, {
destination: file.name
}).then(() => {
resolve(); // Whole thing completed successfully.
}).catch((err) => {
reject('Failed to upload: ' + JSON.stringify(err));
});
});
}).then(() => {
res.status(200).send('Yay!');
return null
}).catch(err => {
console.error('Error while parsing form: ' + err);
res.status(500).send('Error while parsing form: ' + err);
});
});
Lastly, you may want to consider using the Cloud Storage for Firebase in uploading your file instead of Cloud functions. Cloud Storage for Firebase allows you to upload files directly to it, and would work much better:
It has access control
It has resumable uploads/downloads (great for poor connectivity)
It can accept files of any size without timeout-issues
If you want to trigger a Cloud Function on file upload even, you can
do that and a lot more
I managed this by downloading the file to the tmp instead.
You will need:
const mkdirp = require('mkdirp-promise');
Then, inside onChange. I created tempLocalDir like so:
const LOCAL_TMP_FOLDER = '/tmp/';
const fileDir = (the name of the file); //whatever method you choose to do this
const tempLocalDir = `${LOCAL_TMP_FOLDER}${fileDir}`;
Then I use mkdirp to make the temp directory
return mkdirp(tempLocalDir).then(() => {
// Then Download file from bucket.
const bucket = gcs.bucket(object.bucket);
return bucket.file(filePath).download({
destination: tempLocalFile
}).then(() => {
console.log('The file has been downloaded to', tempLocalFile);
//Here I have some code that converts images then returns the converted image
//Then I use
return bucket.upload((the converted image), {
destination: (a file path in your database)
}).then(() => {
console.log('JPEG image uploaded to Storage at', filePath);
})//You can perform more actions of end the promise here
I think my code achieves that you were trying to accomplish. I hope this helps; I can offer more code if necessary.