File not uploading in Express JS using Multer received fro Editor - node.js

I am getting the file using TinyMCE Editor. Using the paste plugin and uploading it using Express Server. I have tried using different things. But need to upload file received from TinyMCE editor
Here is my front-end Code:
function example_image_upload_handler(blobInfo, success, failure, progress) {
var xhr, formData;
localStorage.myfile = blobInfo.blob()
xhr = new XMLHttpRequest();
xhr.withCredentials = false;
xhr.open('POST', 'http://localhost:5000/api/upload');
xhr.upload.onprogress = function (e) {
progress(e.loaded / e.total * 100);
};
xhr.onload = function () {
var json;
if (xhr.status === 403) {
failure('HTTP Error: ' + xhr.status, { remove: true });
return;
}
if (xhr.status < 200 || xhr.status >= 300) {
failure('HTTP Error: ' + xhr.status);
return;
}
json = JSON.parse(xhr.responseText);
if (!json || typeof json.location != 'string') {
failure('Invalid JSON: ' + xhr.responseText);
return;
}
success(json.location);
};
xhr.onerror = function () {
failure('Image upload failed due to a XHR Transport error. Code: ' + xhr.status);
};
formData = new FormData();
formData.append('file', localStorage.myfile, blobInfo.filename());
xhr.send(formData);
};
const handleSubmit = async (e) => {
e.preventDefault();
console.log("hello");
example_image_upload_handler();
console.log(desc);
const newPost = {
username: user.username,
title,
desc,
};
if (file) {
const data = new FormData();
const filename = Date.now() + file.name;
data.append("name", filename);
data.append("file", file);
newPost.photo = filename;
try {
console.log("hello");
if (await axios.post("http://localhost:5000/api/upload", data)) {
// console.log("monu");
}
} catch (err) {
console.log(err);
}
}
try {
const res = await axios.post("http://localhost:5000/api/posts", newPost);
window.location.replace("/post/" + res.data._id);
} catch (err) {
console.log(err);
}
};
const handleEditorChange = (e) => {
console.log(
'Content was updated:',
setDesc(e.target.getContent())
);
}
return (
<div className="write">
{file && (
<img className="writeImg" src={URL.createObjectURL(file)} alt="" />
)}
<form className="writeForm" onSubmit={handleSubmit}>
<div className="writeFormGroup">
<label htmlFor="fileInput">
<i className="writeIcon fas fa-plus"></i>
</label>
<input
type="file"
id="fileInput"
style={{ display: "none" }}
onChange={(e) => setFile(e.target.files[0])}
/>
<input
type="text"
placeholder="Title"
className="writeInput"
autoFocus={true}
onChange={e => setTitle(e.target.value)}
/>
</div>
<Editor
apiKey="XXXXXXXXXXXXXXXXXXXXXXX"
initialValue="<p>Initial content</p>"
init={{
height: 500,
menubar: 'edit',
selector: 'textarea',
plugins: [
'advlist autolink lists link image',
'charmap print preview anchor help',
'searchreplace visualblocks code',
'insertdatetime media table paste wordcount',
'image imagetools paste'
],
images_upload_handler: example_image_upload_handler,
toolbar:
'undo redo | formatselect | bold italic | \
alignleft aligncenter alignright | \
bullist numlist outdent indent | image | help',
paste_data_images: true
}}
onChange={handleEditorChange}
/>
</div>
<button className="writeSubmit" type="submit">
Publish
</button>
</form>
</div>
);
Following is my express js upload API code:
const upload = multer({ storage: storage });
app.post("/api/upload", upload.single("file"), (req, res) => {
console.log("req");
console.log(req);
res.status(200).json("File has been uploaded");
});
Following is the error I am getting:
Please help me with the same.

Related

Multer not able to get the filename

I'm not able to get the filename or path from Multer. This is what I've done so far:
uploadcsv.js
const fileUpload = multer({
limits: 500000,
storage:multer.diskStorage({
destination: (req, file, cb) =>{
cb(null,'upload/csv')
},
filename: (req, file, cb) =>{
const ext = MIME_TYPE_MAP[file.mimetype]
cb(null, uuid + '.' + ext)
},
fileFilter: (req, file, cb) =>{
const isValid = !!MIME_TYPE_MAP[file.mimetype]
let error = isValid ? null : new Error('Invalid mime type')
cb(error, isValid)
}
})
})
Then the route api:
router.post('/contact/importcontact', JWTAuthenticationToken, fileUpload.single('csv'), async (req, res) => {
console.log(req.body)
console.log(req.file.filename)
const csvFilePath = req.file.filename
const stream = fs.createReadStream(csvfile);
const account= await Account.findOne({ acctid: { "$eq": req.body.acctid } })
try {
if (req.file == undefined)
return res.status(400).send({ msg: 'No files were uploaded.' });
csvtojson()
.fromFile(csvFilePath)
.then((jsonObj) => {
console.log(jsonObj);
})
// Async / await usage
const jsonArray = await csvtojson().fromFile(csvFilePath);
res.json({ success: "Uploaded Successfully", status: 200 })
} catch (error) {
res.json({ message: error })
}
})
Lastly, the react importcustomer.js
const handleCSVChange = (e) => {
console.log(e.target.files[0])
setCsvData(e.target.files[0])
setUploadButtonData(e.target.files[0].name)
}
const uploadCSVData = async (e) => {
setLoading(true)
const formData = new FormData();
formData.append("csv", csvData);
console.log(formData)
e.preventDefault()
const response = await Axios.post(process.env.REACT_APP_FETCH_URL + '/api/contact/importcontact', { formData: formData, acctid: lsData.acctid}, { withCredentials: true })
if (response.data.statusCode === "409") {
setMessage(response.data.msg)
setLoading(false)
}
else if (response.data.statusCode === "200") {
setLoading(false)
//history.push('/sources')
}
}
return (
<div className="col-12 grid-margin stretch-card">
<div className="card">
<div className="card-body">
<h4 className="card-title">Import Customer Data From CSV</h4>
<form className="forms-sample" enctype="multipart/form-data">
<div className="form-group">
<label for="files" className="btn btn-primary">{uploadButtonData}
<input id="files" type="file" name="csv" className="form-control" hidden accept="*.csv" onChange={handleCSVChange} /></label>
</div>
<button className="btn btn-primary" onClick={uploadCSVData} style={{ float: "right", width: "7rem" }} type="button">
{loading && <i className="fa fa-refresh fa-spin"></i>}
Upload CSV</button>
<div className="mt-3" style={{ textAlign: "center" }}>
<span id="msg" style={{ color: "red" }}>{message}</span>
</div>
</form>
</div>
</div>
</div>
)
Though I'm able to console.log(req.body) and console.log(e.target.files[0]) and getting the acctid and filename but returned empty for the console.log(formData) and console.log(req.file.filename) returned undefined. What have I missed? Many thanks in advance and greatly appreciate any helps. Thanks again
I have managed to solves this by appending the acctid to formData:
const formData = new FormData();
formData.append("csv", csvData);
formData.append("accctid", lsData.acctid);
const response = await Axios.post(process.env.REACT_APP_FETCH_URL + '/api/contact/importcontact', formData, { withCredentials: true })

AWS S3 Heroku nodejs

I am running a nodejs application hosted on heroku where I want to be able to upload selected photos to AWS S3. I followed all the steps heroku gave, but am finding this error: "MissingRequiredParameter: Missing required key 'Bucket' in params"
Here's my code:
index.js
app.get('/sign-s3', (req, res) => {
const s3 = new aws.S3();
const fileName = req.query['file-name'];
const fileType = req.query['file-type'];
console.log(fileName, fileType)
const s3Params = {
Bucket: S3_BUCKET,
Key: fileName,
Expires: 60,
ContentType: fileType,
ACL: 'public-read'
};
s3.getSignedUrl('putObject', s3Params, (err, data) => {
if (err) {
console.log(err);
return res.end();
}
const returnData = {
signedRequest: data,
url: `https://${S3_BUCKET}.s3.amazonaws.com/${fileName}`
};
res.write(JSON.stringify(returnData));
res.end();
});
});
app.post('/save-details', (req, res) => {
res.send(req.body.username)
console.log(req.body.username)
});
here's my front end scripts
testing.ejs
<input type="file" id="file-input">
<p id="status">Please select a file</p>
<img style="border:1px solid gray;width:300px;" id="preview" src="/images/default.png">
<h2>Your information</h2>
<form method="POST" action="/save-details">
<input type="hidden" id="avatar-url" name="avatar-url" value="/images/default.png">
<input type="text" name="username" placeholder="Username"><br>
<input type="text" name="full-name" placeholder="Full name"><br><br>
<hr>
<h2>Save changes</h2>
<input type="submit" value="Update profile">
</form>
<script>
/*
Function to carry out the actual PUT request to S3 using the signed request from the app.
*/
function uploadFile(file, signedRequest, url){
const xhr = new XMLHttpRequest();
xhr.open('PUT', signedRequest);
xhr.onreadystatechange = () => {
if(xhr.readyState === 4){
if(xhr.status === 200){
document.getElementById('preview').src = url;
document.getElementById('avatar-url').value = url;
}
else{
alert('Could not upload file.');
}
}
};
xhr.send(file);
}
/*
Function to get the temporary signed request from the app.
If request successful, continue to upload the file using this signed
request.
*/
function getSignedRequest(file){
const xhr = new XMLHttpRequest();
xhr.open('GET', `/sign-s3?file-name=${file.name}&file-type=${file.type}`);
xhr.onreadystatechange = () => {
if(xhr.readyState === 4){
if(xhr.status === 200){
const response = JSON.parse(xhr.responseText);
uploadFile(file, response.signedRequest, response.url);
}
else{
alert('Could not get signed URL.');
}
}
};
xhr.send();
}
/*
Function called when file input updated. If there is a file selected, then
start upload procedure by asking for a signed request from the app.
*/
function initUpload(){
const files = document.getElementById('file-input').files;
const file = files[0];
if(file == null){
return alert('No file selected.');
}
getSignedRequest(file);
}
/*
Bind listeners when the page loads.
*/
(() => {
document.getElementById('file-input').onchange = initUpload;
})();
</script>
hopefully this helps, and thanks in advance.

Trying to send formData with another parameter via axios

I have a form that uploads single files via a FormData object. In addition to the file attribute, I want to also assign this file to a Group.
I can't figure out how to add the group object id to the request.
Here is my upload component:
<template>
<div>
<div class="ui attached segment rounded mb-3">
<form #submit.prevent="sendFile" enctype="multipart/form-data" class="ui form">
<div v-if="message" class="`message ${error ? 'text-danger' : 'text-success'}`">
<div class="message-body"> {{ message }}</div>
</div>
<div class="field">
<label for="file" class="label">Upload File</label>
<input
type="file"
ref="file"
#change="selectFile"
class="file-input"
/>
<!-- <span class="file-cta">
<span class="file-icon">
<i class="fas fa-upload"></i>
</span>
<span class="file-label">
Choose a file...
</span>
</span> -->
<span v-if="file" class="file-name">{{file.name}}</span>
<div class="field">
<button class="btn btn-info">Send</button>
</div>
</div>
</form>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: "SimpleUpload",
data() {
return {
file: "",
message: '',
error: false
}
},
computed: {
currentGroup() {
return this.$store.getters.currentGroup;
}
},
methods: {
selectFile() {
const file = this.$refs.file.files[0];
const allowedTypes = ["image/jpeg", "image/png", "image/gif"];
const MAX_SIZE = 200000;
const tooLarge = file.size > MAX_SIZE;
if (allowedTypes.includes(file.type) && !tooLarge) {
this.file = file;
this.error = false;
this.message = '';
} else {
this.error = true;
this.message = tooLarge ? `Too large. Max size is ${MAX_SIZE/1000}kb` : "Only images are allowed"
}
},
async sendFile() {
const formData = new FormData();
formData.append('file', this.file);
formData.append('group', this.currentGroup);
console.log('file is: ', this.file)
console.log('this is form data after file is added: ', formData)
try {
await axios.post('/uploads', {
formData,
group_id: this.currentGroup.id
});
console.log('this is the formData being sent: ', formData)
this.message = 'File has been uploaded';
this.file = '';
this.error = false;
} catch(err) {
this.message = err.response.data.error;
this.error = true
}
}
}
}
</script>
Here is my route in :
const Group = require('../models/Group');
const Upload = require('../models/Upload');
const router = express.Router();
const upload = require('../services/file-uploads');
const singleUpload = upload.single('file'); // has to match front end
router.post('/', auth.required, async function(req, res) {
console.log ('upload route is hit')
let results = await singleUpload(req, res, async function (err) {
if (err) {
return res.status(422).send({ errors: [{title: 'File upload error, detail: err.message'}] });
} else {
console.log('this is the upload reqest fobject: ', req);
console.log("trying to save data to postgres");
// console.log('this is the originalname of the reqest file object: ', req.file);
console.log('this is the upload reqest body: ', req.body.group_id);
await Upload.query()
.insert({
name: req.body.file.originalname,
users_id: req.user.id,
filetype: req.body.file.mimetype,
filesize: req.body.file.size,
file_url: req.body.file.location,
groups_id: req.body.group_id
})
res.json({
success: true,
message: 'ok'
});
res.json(results)
}
})
});
module.exports = router;
and the upload config in services/file-uploads:
const aws = require('aws-sdk');
const multer = require('multer');
const multerS3 = require('multer-s3');
aws.config.update({
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
region: 'us-east-1'
})
const s3 = new aws.S3();
const fileFilter = (req, file, cb) => {
if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
cb(null, true)
} else {
cb(new Error('Invalid Mime Type, only JPEG and PNG'), false);
}
}
const upload = multer({
storage: multerS3({
s3: s3,
bucket: process.env.AWS_BUCKET,
acl: 'public-read',
metadata: function (req, file, cb) {
cb(null, {fieldName: 'TESTING_META_DATA!'});
},
key: function (req, file, cb) {
cb(null, Date.now().toString())
}
})
})
module.exports = upload;
In the request, I see body: { formData: {}, group_id: 1 },
and I can retrieve the group_id with req.body.id, but the formData object is empty
Prepare the FormData like this:
const formData = new FormData();
formData.append('file', this.file); // Append a file
formData.append('group_id', this.currentGroup.id); // Append a field
And then send it like this:
await axios({
method: 'post',
url: '/uploads',
data: formData,
headers: {
'Content-Type': 'multipart/form-data'
}
});

NodeJS render html file with form not working on angular side

I am using ExpressJS with EJS template view engine. I am trying to show an HTML file on the angular component, but the form tag and its child input tag do not work on the angular side. They show only label data.
On NodeJS
agreementController.js
exports.getAgreementHtml = async (request, response, next) => {
const params = request.query
let reqPath = path.join(__dirname, '../agreements');
var agreementObj = {
user: { email: "example#gmail.com" }
}
// render domestic rent html
ejs.renderFile(reqPath + '/domestic_rent.ejs', agreementObj, {}, function (err, str) {
if (err !== null) {
responseObj.status = errorCodes.DATA_NOT_FOUND
responseObj.message = language.getMessage('NO_RECORD_FOUND')
response.send(responseObj)
return
}
responseObj.status = errorCodes.OK
responseObj.data = str
response.send(responseObj);
return;
});
}
domestic_rent.js
<form>
<div class="form-group">
<p><%= user.email %></p>
<div class="col-sm-offset-2 col-sm-10">
<input type="text" class="form-control" id="inputEmail3" placeholder="test" required name="test">
</div>
</div>
</form>
On Angular 8 Side
agreement-show.component.ts
getAgreementData() {
const params = {
id: this.agreementId
};
this.agreementService.getAgreementHtml(params).subscribe(
(result) => {
console.log('result agreement data::: ', result);
if (result.status !== 200) {
this.commonService.change.emit({ status: 'error', message: 'unknown error' });
return;
}
this.someHtml = result.data;
return;
}, (error) => {
console.log('error', error)
this.commonService.change.emit({ status: 'error', message: error.message });
}
);
}
agreement-show.component.html
<div [innerHTML]="someHtml"></div>
Output Attachment
By using ElementRef function we can add html runtime.
Please use following step:
#ViewChild('showitems') showitems: ElementRef;
const elemt: HTMLElement = this.showitems.nativeElement;
this.someHtml = result.data;
elemt.innerHTML = this.someHtml;

store images in postgresql from react using express multer

I'm trying to save images from my react app to backend using nodejs(express) and multer, I tried from these How to upload image using javascript fetch api and express multer
after I got the message file uploaded, how do you store it to database?
Here's the code in my react app
class UploadCover extends React.Component {
constructor(props){
super(props);
this.state={
id: this.props.location.state.id,
selectedFile : '',
imagePreviewUrl: ''
}
}
//handle image change
handleImageChange = (e) => {
e.preventDefault();
let reader = new FileReader();
let file = e.target.files[0];
reader.onloadend = () =>{
this.setState({
selectedFile:file,
imagePreviewUrl: reader.result
});
};
reader.readAsDataURL(file);
this.handleUploadCover(e.target.files[0]);
}
//upload cover
handleUploadCover = (file) => {
let form = new FormData(this.refs.myForm);
form.append('myImage', file);
fetch('http://localhost:3001/upload-cover', {
method: 'post',
header: { "Content-Type" : "application/json"},
body: form
})
.then( res =>res.json())
.then(data => console.log("data: ", data))
};
render() {
const { classes, ...rest } = this.props;
const { id, imagePreviewUrl } = this.state;
console.log("url: ", imagePreviewUrl)
let $imagePreview = null;
if(imagePreviewUrl){
$imagePreview = <img style={{width: "100%", height: "100%"}} src={imagePreviewUrl}/>;
}else{
$imagePreview = (
<div className={classes.previewText}>Please select cover</div>
);
}
return (
<div>
<HeaderHome/>
<div className={classNames(classes.main, classes.mainRaised)}>
<div className={classes.container}>
<div className={classes.storymargin}>
<h2 className={classes.title}>Upload Cover Story</h2>
<Card className={classes.uploadcontainer}>
<CardContent>
<div className={classes.imgPreview}>{$imagePreview}</div>
<form ref='myForm' encType='multipart/form-data'>
<input
accept="image/*"
className={classes.input}
id="raised-button-file"
multiple
type="file"
onChange={ e => this.handleImageChange(e)}
// ref='myForm'
/>
</form>
<label htmlFor="raised-button-file">
<Button component="span" className={classes.buttonupload}>
Choose Image
</Button>
</label>
</CardContent>
<CardActions>
<Link
to={{
pathname: "/create-chapter",
state: {id : id}
}}
>
<Button size="small" clor="primary" className={classes.buttonnextup}
onClick={ e => this.handleUploadCover(e) }
>
Next
</Button>
</Link>
</CardActions>
</Card>
</div>
</div>
</div>
<div className={classes.footer}>
<Footer />
</div>
</div>
);
}
}
and here's the backend
//set storage engine
const storage = multer.diskStorage({
destination: './public/uploads',
filename: (req, file, cb) => {
cb(null, file.fieldname + '-' + Date.now() + path.extname(file.originalname));
}
})
//Init upload
const upload = multer({
storage: storage,
limits: {fileSize: 1000000},
fileFilter: (req, file, cb) => {
checkFileType(file, cb);
}
}).single('myImage');
//Check File Type
checkFileType = (file, cb) => {
//Allowed ext
const filetypes = /jpeg|jpg|png|gif/;
//Check ext
const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
//Check mime
const mimetype = filetypes.test(file.mimetype);
if(mimetype && extname){
return cb(null, true);
}else{
cb('Error: Images Only');
}
}
//upload cover story
app.post('/upload-cover', (req, res) => {
console.log('handling upload image');
upload( req, res, (err) => {
if(err){
console.log('first err', err);
res.send({
msg: err
});
}else{
if(req.file === undefined){
console.log('Error: No File Selected');
res.send({
msg: 'Error: No File Selected'
});
}else{
console.log('File Uploaded');
res.send({
msg: 'File Uploaded',
file: `uploads/${req.file.filename}`
});
}
}
});
});
and how can i send request with content type application/json and multipart/form-data at the same time through body request, I want to send form and id:this.state.id

Resources