I am trying to upload an image to mongodb using angular and nodejs. The code is below. I got the backend working but the problem is with the html input i get 'C:fakepath/file.xyz'. I was looking online and saw that there is not a way to get the relative path of the file. Can someone please tell me how i can change my front end code to get and send the file path to the backend to then save. I read that the browser doesnt allow relative path of the file but then how can I upload. Thanks!
The nodejs image save method is:
async function SaveImage(userParam) {
const entry = new imageEntries(userParam);
entry.image.data = fs.readFileSync(userParam.imagePath);
entry.image.contentType = 'image/png';
await entry.save();
}
The html code is:
<div class="upload-btn-wrapper">
<button class="btn">Upload a file</button>
<input type="file" name="myfile" id="myFile" />
</div>
What I pass as the path in the backend is:
ImageJournal.imagePath = (<HTMLInputElement>document.getElementById('myFile')).value;
but with the code above i get the following error:
ENOENT: no such file or directory, open 'C:\fakepath\chapter9problemsandanswers.doc'
Ok here it is:
HTML:
<div class="form-group">
<label for="pdf">PDF</label>
<input type="file" id="pdf" (change)="onFileChange($event)" #fileInput>
<button type="button" class="btn btn-sm btn-default" (click)="clearFile()">clear file</button>
</div>
TS:
import { Component, OnInit, ElementRef, ViewChild } from '#angular/core';
#ViewChild('fileInput') fileInput: ElementRef;
public image;
onFileChange(event) {
let reader = new FileReader();
if(event.target.files && event.target.files.length > 0) {
let file = event.target.files[0];
reader.readAsDataURL(file);
let value = <String>reader.result;
reader.onload = () => {
this.image = {
filename: file.name,
filetype: file.type,
value: value.split(',')[1]
};
};
}
}
Then send the image with http post with a service.
On the server:
// send file
app.post('/api/sendFile', function (req, res) {
File.create({
filename: req.body.filename,
filetype: req.body.filetype,
value: req.body.value
}, function (err, file) {
if (err)
res.send(err);
else {
const response = {
name: file.filename
}
res.send(response);
}
});
});
This is written with mongoDb and mongoose and I have a model named File.
This is all it need to save it.
Related
I'm trying to create an app where I post a text and a photo to my twitter feed.
I'm using Node.js 16.17.1, Angular 15.1.3, Twitter-Api-V2
I get the following error:
ReferenceError: Can't find variable: Buffer
I cannot find the problem and I have followed the documentation of the API.(https://github.com/PLhery/node-twitter-api-v2/blob/master/doc/examples.md)
My code:
homecomponent.ts
async tweet(tweetText: string, file: File) {
console.log("Tweet funkction: " + tweetText)
this.twitter.readWrite
try {
const media = await Promise.all([
this.twitter.v1.uploadMedia(Buffer.from(file.name), { type: 'jpg' })
]);
await this.twitter.v1.tweet(tweetText, { media_ids: media })
} catch(e) {
console.log(e)
}
}`
homecomponent.html
<div class="text-center">
<input class="form-control" type="file"
(change)="onChange($event)">
<input mat-input type="text" #tweetText placeholder="Tweet text">
<button mat-raised-button (click)="onUpload(tweetText.value)"
class="btn btn-success">
Upload Photo to Twitter
</button>
</div>
I've developed an app that's uploaded to Github and I'm using Heroku to host the (Backend folder) from Github using (automatic deployment) and also using Netlify to host the (Frontend folder) and it's working great in my local computer, but when I try to upload files from my form in frontend it sends a request to the backend and the backend it self saves the file to /uploads folder that's located in frontend directory.
My file structure is like this:
[Server]
- controllers
- - food.js
[Client]
- public
-- uploads
- src
-- pages
--- dashboard
---- food
----- AddFood.js
it's working great on localhost, and this is my code:
(client)
AddFood.js:
import { useState, useEffect } from 'react'
import { Link } from 'react-router-dom'
import Axios from 'axios'
import useDocumentTitle from '../../../hooks/useDocumentTitle'
import Modal from '../../../components/Modal/Modal'
import { Success, Error, Loading } from '../../../components/Icons/Status'
import { createSlug } from '../../../functions/slug'
import goTo from '../../../functions/goTo'
const AddFood = () => {
useDocumentTitle('Add Food')
//Form States
const [foodName, setFoodName] = useState('')
const [foodPrice, setFoodPrice] = useState('')
const [foodDesc, setFoodDesc] = useState('')
const [foodFile, setFoodFile] = useState('')
const [preview, setPreview] = useState()
const [addFoodStatus, setAddFoodStatus] = useState()
const [addFoodMessage, setAddFoodMessage] = useState()
//Form errors messages
const ImgErr = document.querySelector('[data-form-img-msg]')
const foodNameErr = document.querySelector('[data-form-name-msg]')
const priceErr = document.querySelector('[data-form-price-msg]')
const descErr = document.querySelector('[data-form-desc-msg]')
const formMsg = document.querySelector('[data-form-msg]')
const modalLoading = document.querySelector('#modal')
const BASE_URL =
process.env.NODE_ENV === 'development'
? process.env.REACT_APP_API_LOCAL_URL
: process.env.REACT_APP_API_URL
const updateFoodImg = e => {
const file = e.target.files[0]
if (file) {
const fileType = file.type.split('/')[0]
if (fileType === 'image') setFoodFile(file)
const fileSizeToMB = file.size / 1000000
const MAX_FILE_SIZE = 1 //mb
if (fileSizeToMB > MAX_FILE_SIZE) {
if (ImgErr)
ImgErr.textContent = `file size can't be more than ${MAX_FILE_SIZE} MB`
} else {
ImgErr.textContent = ''
}
}
}
useEffect(() => {
// if there's an image
if (foodFile) {
const reader = new FileReader()
reader.onloadend = () => setPreview(reader.result)
reader.readAsDataURL(foodFile)
} else {
setPreview(null)
}
}, [foodFile])
const handleAddFood = async e => {
e.preventDefault()
//using FormData to send constructed data
const formData = new FormData()
formData.append('foodName', foodName)
formData.append('foodPrice', foodPrice)
formData.append('foodDesc', foodDesc)
formData.append('foodImg', foodFile)
if (
ImgErr.textContent === '' &&
foodNameErr.textContent === '' &&
priceErr.textContent === '' &&
descErr.textContent === ''
) {
//show waiting modal
modalLoading.classList.remove('hidden')
try {
const response = await Axios.post(`${BASE_URL}/foods`, formData)
const { foodAdded, message } = response.data
setAddFoodStatus(foodAdded)
setAddFoodMessage(message)
//Remove waiting modal
setTimeout(() => {
modalLoading.classList.add('hidden')
}, 300)
} catch (err) {
formMsg.textContent = `Sorry something went wrong ${err}`
}
} else {
formMsg.textContent = 'please add all details'
}
}
return (
<>
{addFoodStatus === 1 ? (
<Modal
status={Success}
msg='Added food'
redirectLink='menu'
redirectTime='3000'
/>
) : addFoodStatus === 0 ? (
<Modal
status={Error}
msg={addFoodMessage}
msg=''
/>
) : null}
<section className='py-12 my-8 dashboard'>
<div className='container mx-auto'>
<h3 className='mx-0 mt-4 mb-12 text-2xl text-center'>Add food</h3>
<div>
<div className='food'>
{/* Show Modal Loading when submitting form */}
<Modal
status={Loading}
modalHidden='hidden'
classes='text-blue-500 text-center'
msg='Please wait'
/>
<form
method='POST'
className='form'
encType='multipart/form-data'
onSubmit={handleAddFood}
>
<label className='flex flex-wrap items-center justify-center gap-4 mb-8 sm:justify-between'>
<img
src={
preview === null
? 'https://source.unsplash.com/random?food'
: preview
}
alt='food' //change with food image name
className='object-cover p-1 border border-gray-400 w-28 h-28 dark:border-gray-300 rounded-xl'
/>
<input
type='file'
name='foodImg'
id='foodImg'
accept='image/*'
onChange={updateFoodImg}
className='grow-[.7] cursor-pointer text-lg text-white p-3 rounded-xl bg-orange-800 hover:bg-orange-700 transition-colors'
required
/>
<span
className='inline-block my-2 text-red-400 font-[600]'
data-form-img-msg
></span>
</label>
<label htmlFor='foodName' className='form-group'>
<input
type='text'
id='foodName'
className='form-input'
autoFocus
required
onChange={e => setFoodName(createSlug(e.target.value.trim()))}
/>
<span className='form-label'>Food Name</span>
<span
className='inline-block my-2 text-red-400 font-[600]'
data-form-name-msg
></span>
</label>
<label htmlFor='foodPrice' className='form-group'>
<input
type='number'
id='foodPrice'
className='form-input'
min='5'
max='500'
required
onChange={e => setFoodPrice(e.target.value.trim())}
/>
<span className='form-label'>Price</span>
<span
className='inline-block my-2 text-red-400 font-[600]'
data-form-price-msg
></span>
</label>
<label htmlFor='foodDescription' className='form-group'>
<textarea
name='foodDescription'
id='foodDescription'
minLength='10'
maxLength='300'
className='form-input'
required
onChange={e => setFoodDesc(e.target.value.trim())}
></textarea>
<span className='form-label'>Description</span>
<span
className='inline-block my-2 text-red-400 font-[600]'
data-form-desc-msg
></span>
</label>
<div
className='my-14 text-red-400 font-[600] text-center text-xl'
data-form-msg
></div>
<div className='flex items-center justify-evenly'>
<button
type='submit'
className='min-w-[7rem] bg-green-600 hover:bg-green-700 text-white py-1.5 px-6 rounded-md'
>
Add
</button>
<Link
to={goTo('menu')}
className='text-gray-800 underline-hover text-bold dark:text-white'
>
Food Menu
</Link>
</div>
</form>
</div>
</div>
</div>
</section>
</>
)
}
export default AddFood
(server)
foods.js:
const FoodsModel = require(`${__dirname}/../models/food-model.js`)
const { v4: uuidv4 } = require('uuid')
const sharp = require('sharp')
const deleteFile = require('../functions/deleteFile')
const addFood = async (req, res) => {
const { foodName, foodPrice, foodDesc } = req.body
const { foodImg } = req.files
const foodImgName = uuidv4() + foodImg.name
const foodImgMovePath = `${__dirname}/../../client/public/uploads/${foodImgName}.webp`
const foodImgDisplayPath = `/uploads/${foodImgName}`
const foods = new FoodsModel({
foodImgDisplayPath,
foodName,
foodPrice,
foodDesc
})
sharp(foodImg.data)
.rotate()
.resize(200)
.jpeg({ mozjpeg: true, quality: 50 })
.toBuffer()
.then(newBuffer => {
//changing the old jpg image buffer to new webp buffer
foodImg.data = newBuffer
foodImg.mv(foodImgMovePath, err => {
if (err) {
res.json({
message: `Sorry something wrong with server! 😥: ${err}`
})
return
}
foods.save()
res.json({
message: 'Food Added Successfully',
foodAdded: 1
})
})
})
.catch(err => {
res.json({
//https://mhmdhidr-restaurant.netlify.app/uploads/20cc09a0-1811-48b0-bffa-49e7a1981537chicken-legs.webp
message: `Sorry! Something went wrong, check the error => 😥: \n ${err}`,
foodAdded: 0
})
})
}
const getFood = async (req, res) => {
res.json(res.paginatedResults)
}
const deleteFood = async (req, res) => {
const { prevFoodImg } = req.body
const { foodId } = req.params
deleteFile(prevFoodImg)
try {
await FoodsModel.findByIdAndRemove(foodId)
res.json({
message: 'Food Deleted Successfully',
foodDeleted: 1
})
} catch (error) {
res.json({
message: `Sorry! Something went wrong, check the error => 😥: \n ${error}`,
foodDeleted: 0
})
}
}
const updateFood = async (req, res) => {
const { foodName, foodPrice, foodDesc, prevFoodImg } = req.body
const { foodId } = req.params
const { foodImg } = req.files || ''
const foodImgName = uuidv4() + foodImg?.name || ''
const foodImgMovePath = `${__dirname}/../../client/public/uploads/${foodImgName || ''}`
const foodImgDisplayPath =
foodImg !== '' && foodImg !== undefined ? `/uploads/${foodImgName}` : prevFoodImg
try {
await FoodsModel.findByIdAndUpdate(foodId, {
foodImgDisplayPath,
foodName,
foodPrice,
foodDesc
})
if (foodImg !== '' && foodImg !== undefined) {
deleteFile(prevFoodImg)
foodImg.mv(foodImgMovePath, err => {
if (err) {
res.json({ message: `Sorry something wrong with server! 😥: ${err}` })
}
})
}
res.json({
message: 'Food Updated Successfully',
foodUpdated: 1
})
} catch (error) {
res.json({
message: `Sorry! Something went wrong, check the error => 😥: \n ${error}`,
foodUpdated: 0
})
}
}
module.exports = { addFood, getFood, deleteFood, updateFood }
But when I try to upload a file in the Netlify app this error shows up:
Error: ENOENT: no such file or directory, open '/app/controllers/../../client/public/uploads/9631bb96-e41d-4c9a-aa35-d22b551ab662MASHAWI-IN-DUBAI.jpeg.webp'
I've tried to Google it a lot but unfortunately didn't find a solution.
Thanks for your help.
A two part answer:
Your back-end has no business putting files into your front-end's directory structure.
A better choice might be to use an uploads/ folder in the back-end project, exposing those over HTTPS, and linking to them from your front-end.
But that won't work on Heroku due to its ephemeral filesystem.
An even better choice would be to save them to a cloud-based object store like Amazon S3 or Azure Blob Storage, or a more specialized service like Cloudinary if they're images. Heroku tends to recommend S3.
Your back-end now just needs to store the URL to each file and provide that link to your front-end upon request.
Even on other hosts that allow you to save files into your back-end's filesystem, using a third-party service has many benefits. You can trivially scale horizontally (adding new nodes), your application becomes less stateful, etc.
User uploads never belong in your code repository, no matter how and where you choose to host them. They are content, not code, and should not be tracked and versioned alongside your code.
I am trying to upload files to server using Angular and Node, using multer.
I have Todo Model as :
export class TodoModel {
todo_id !:number;
todo_title !:string;
todo_description !:string;
todo_status !:number;
todo_deleted_flag !:boolean;
todo_image !:Object;
}
todo.component.ts
title:string;
desc:string;
selected_image:File = null;
fileUploadListener(event){
//console.log(event)
//console.log(event.target.files[0])
this.selected_image = <File>event.target.files[0]
console.log(this.selected_image)
}
onSubmit(form:NgForm){
const fd = new FormData()
if(this.selected_image) {
fd.append('todo_image',this.selected_image,this.selected_image.name)
}
console.log(fd);
const todo_model : TodoModel = {
todo_id: null,
todo_title:this.title,
todo_description:this.desc,
todo_status:1,
todo_deleted_flag:false,
todo_image:null
}
console.log(fd);
this.todoAdd.emit(todoadded);
this.todoAdd_DB.emit(todo_model);
this.addTodo_DB(todo_model, fd)
form.resetForm();
}
addTodo_DB(todo_db: TodoModel, fileUpload:Object){
//const todo_db
return this.http.post<{message:any}>('http://localhost:3000/api/todos/post_all_todos_db', todo_db,fileUpload).subscribe(data => {
console.log(data.message);
console.log(todo_db);
})
}
todo.component.html
<div class="col-md-12">
<form (ngSubmit)="onSubmit(todoForm)" #todoForm="ngForm">
<div class="mb-3">
<label for="todo_title" class="form-label">Title</label>
<input type="text" class="form-control" id="todo_title" [(ngModel)]="title" name="title">
</div>
<div class="mb-3">
<label for="label" class="form-label">Description</label>
<textarea class="form-control" id="todo_description" [(ngModel)]="desc" name="desc"></textarea>
</div>
<div class="mb-3">
<label for="todo_image" class="form-label">Image</label>
<input type="file" class="form-control" id='todo_image' (change)="fileUploadListener($event)">
</div>
<button type="submit" class="btn btn-success">Add To Do</button>
</form>
</div>
</div>
And on Server Side, using Node Js and PgSQL :-
app.post('/api/todos/post_all_todos_db',upload_using_multer.single('todo_images') , (req, res, next) => {
// const todo_post = req.body;
const files = req.file;
console.log(files) // - ----------> This does NOT work
console.log(req.body) //------> this works
//PGSQL insert query here
res.status(201).json({
message:"Post Added Successfully"
})
})
While doing console.log() in Angular side, I am getting the form data, but, on Node Js side, I get it as null.
Almost every tutorial I see, uses only one file upload , and that too, try to submit the form using the Form's action. I dont want to do that, so I tried doing this.
I
i once had the same issue and solved it with formdata, my example uploads multiple files. here is an example:
Node.JS
const serverRoutes = (function () {
const express = require('express');
const router = express.Router();
const multer = require('multer');
const upload = multer();
router.post('/myresource', upload.any(), (req, res) => {
console.log(req.files);
});
return router;
});
on angular
export class DataService {
constructor(private http: HttpClient) { }
sendMyFiles(file): Observable<MyResponse> {
const formData = new FormData();
formData.append("file", file);
return this.http.post<MyResponse>(
`${environment.backendAPI}myresource`,
formData
);
}
}
I have the daturi of an image which is uploaded from the desktop.I would like to send this data uri to express server so as to save the dataUri in a text file. Since the size of the data uri of the image is quite large I am getting payload too large error which is understandable. I tried using multer but I couldn't figure out how to extract the data uri of the image when multer is used, on the server side.Any help on this is greatly appreciated.
Below is some of the code sample that I am trying to use
<div class="row">
<div class="form-group">
<label class="btn btn-default btn-file" for="FileUpload">Upload a Plan</label>
<input type="file" id ="FileUpload" accept="image/*" capture="camera" value="" onchange="readFileURL(this);" style="display: none;">
<img id="chosenFile" src="#" style="visibility: hidden;"/>
</div>
</div>
<div class="row">
<div class="col-sm-12"><button style="background-color: green" class="btn btn-default btn-sm" onclick="handleUplod(this)">Upload</button></div>
</div>
<script type="text/javascript">
function readFileURL(input) {
if (input.files && input.files[0]) {
var reader = new FileReader();
document.getElementById("chosenFile").style.visibility="visible";
reader.onload = function (e) {
$('#chosenFile').attr('src', e.target.result).width(150).height(150);
console.log("result:",e.target.result);
imageData = e.target.result;
};
console.log("data url:"+reader.readAsDataURL(input.files[0]));
}
};
function handleUplod(){
$.ajax({
type: "POST",
url: "/send",
data: { MyPlanDataUri:imageData },
success: function(result){
location.href= "/someplace";
},
error: function(result) {
alert('error');
}
});
};
On the server side I am doing the following
app.post('/send', function(req,res) {
var Tex1 = req.body.MyPlanDataUri;
var newFile = "ImageFile.txt";
fs.writeFile(newFile, Tex1, (err) => {
if (err) res.send(err);
console.log('File saved successfully ! - ', newFile);
}
);
res.send("Successfull");
}
P.S the above code works perfectly fine for small datauri's
I have simple multipart formdata
<form action="/upload" enctype="multipart/form-data" method="post">
<span class="btn btn-file">
<input type="file" name="file" ng-model="file"/>
<span class="btn btn-primary" ng-click="upload()">Upload</span>
</span>
</form>
What I want to do it, post all the information related to file to the server written in node.js
server.js This is file upload handler written in node. Formidable expects all parameters of a file.
upload: function uploadfn (req, res) {
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files) {
// `file` is the name of the <input> field of type `file`
var old_path = files.file.path,
file_size = files.file.size,
file_ext = files.file.name.split('.').pop(),
index = old_path.lastIndexOf('/') + 1,
file_name = old_path.substr(index),
new_path = path.join(process.env.PWD, '/uploads/', file_name + '.' + file_ext);
fs.readFile(old_path, function(err, data) {
fs.writeFile(new_path, data, function(err) {
fs.unlink(old_path, function(err) {
if (err) {
res.status(500);
res.json({'success': false});
} else {
res.status(200);
res.json({'success': true});
}
});
});
});
});
}
The things I'm stuck at is, I have service call ready in angular as follows:
service.factory('FileUpload', function ($resource) {
return $resource('/upload', {}, {
post: {method: 'POST'}
});
});
This call hits the backend from angular controller as follows
$scope.upload = function(){
console.log($scope.file);
FileUpload.post(function(){
});
}
I'm not sure how to post the file submit so that node can catch it. Also $scope.file is undefined.
Please help me solve this.
There's a good directive for file upload for angularjs, try to use it
https://github.com/danialfarid/angular-file-upload