Multipart File upload in MERN Stack - node.js

I am trying to Implement File upload feature using multipart to speed up upload for big file size.
I am unable to figure out why this is not working.
I am providing environment variables via console for now.
When I click send button, nothing happens. Both my servers are running fine (I am quite sure of them)
Issue:
1> when I do
axios.post(`http://localhost:4000`, data)
it throws an error. How shall I post my file (data) to backend?
Please point out the mistakes or things to be done.
Thanks in Advance!
Following is my FileUpload.js
import React, { Component } from 'react';
import axios from 'axios';
class FileUpload extends Component {
constructor (props) {
super(props);
this.submitFile = this.submitFile.bind(this);
this.fileInput = React.createRef();
}
submitFile = (event) => {
event.preventDefault();
var data = {
file: this.fileInput.current.files[0],
name: this.fileInput.current.files[0].name
};
axios.post(`http://localhost:4000`, data)
.then(response => {
console.log(response);
})
.catch(error => {
console.log(error);
});
}
render() {
return (
<form onSubmit={this.submitFile}>
<input type='file' ref={this.fileInput} />
<button type='submit'>Send</button>
</form>
);
}
}
export default FileUpload;
My backend Node upload.js looks like following:
const multer = require('multer');
const multerS3 = require('multer-s3');
const AWS = require('aws-sdk');
var cred = require('../aws/config')
const AWS_SECRET_ACCESS = cred.access();
const AWS_ACCESS_KEY = cred.awskey();
// configure the keys for accessing AWS
AWS.config.update({
accessKeyId: process.env.AWS_SECRET_ACCESS,
secretAccessKey: process.env.AWS_ACCESS_KEY,
region: ''
});
// create S3 instance
const s3 = new AWS.S3();
const upload = multer({
storage: multerS3({
s3:s3,
bucket: '',
metadata: function (req, file, cb) {
cb(null, {fileName: 'uploadit'});
},
key: function (req, file, cb) {
cb(null, Date.now().toString())
}
})
})
module.exports = upload;

I always upload my photos on cloudinary.
You can save them locally too but on the long term I don't think this is a solution.
Backend
import express from 'express';
import webpackMiddleware from 'webpack-dev-middleware';
import webpack from 'webpack';
import webpackConfig from './webpack.config.js';
const app = express();
app.use(webpackMiddleware(webpack(webpackConfig)));
const port = process.env.PORT || 5000;
const cloudinary = require('cloudinary');
cloudinary.config({
cloud_name: 'your cloud name',
api_key: 'your api key',
api_secret: 'your api secret'
});
app.use(multipartMiddleware);
app.post('/images', multipartMiddleware, (req, res) => {
// file upload
let formData = Object.values(req.files);
let photoName = Object.keys(req.files);
cloudinary.uploader.upload(
formData[0].path,
function () { res.json('Uploaded!')},
{
public_id: photoName[0],
crop: 'scale',
width: 1200,
height: 400,
quality: 100,
tags: ['usedCars', 'usedCars']
}
)
});
app.listen(port, () => {
console.log('Your app on port:', port);
});
Frontend
// input type file
<input type="file"
accept="image/*"
onChange={evt => this.updateFileValue(evt)}
id="carPhoto"/>
// updating state
updateFileValue(evt) {
this.setState({
carPhoto: evt.target.files[0]
});
}
// post request
Apis.addCarsList(this.state).then((res) => {
const fd = new FormData();
const photoName = "a name for your photo, I usually get the mongoId as a photo name";
fd.append(res.data, this.state.carPhoto, photoName);
axios.post('/images', fd).then((res) => {
// do w/e
});
})

Apparently you're doing almost everything correct, except for one small detail that caught my attention.
your fileName is defined from files.name, which is undefined (try to console.log that to verify). What you need to do is give it a unique name, like for example a timestamp.
app.post('/upload', (request, response) => {
const form = new multiparty.Form();
form.parse(request, async (error, fields, files) => {
if (error) throw new Error(error);
try {
const path = files.file[0].path;
const buffer = fs.readFileSync(path);
const type = fileType(buffer);
const fileName = Date.now().toString();
const data = await uploadFile(buffer, fileName, type);
return response.status(200).send(data);
}
catch (error) {
return response.status(400).send(error);
}
});
});
Also make sure you're proxying your requests: https://facebook.github.io/create-react-app/docs/proxying-api-requests-in-development
Some additional debugging tricks, if you haven't tried that yet:
Try to create a simple get route in your server just to make sure that client and server are talking to each other.
Try to create first a get request to list your buckets to make sure that the communication with S3 works as expected: https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#listBuckets-property
I hope this helps!
Cheers!

Related

Uploading images to google cloud storage with multer and nodejs getting error

== updated question on 9/9 ===
Tried to use Multer directly without the Middleware like before and using Postman to upload the images.
From Nodejs, req return
files: [Object: null prototype] {
imagebackup: [ [Object] ],
imagebanner: [ [Object] ]
},
However, when I console req.file
it showing "undefined"
new file-routers.js as below:
const express = require('express');
const multer = require('multer');
const router = express.Router();
const upload = multer({
storage: multer.MemoryStorage,
}).fields([{name: "imagebackup"}, {name: "imagebanner"}]);
router.post('/file', (req, res)=>{
upload(req, res, (err) => {
console.log(req) // return Files [object null]
console.log(req.file) // return "undefined"
if(err) throw err;
})
});
**Weird thing is, by using upload.single(), everything works just fine. **
==
==
===== Here is the old code & can't solve it =====
It return an error
MulterError: Unexpected field
at wrappedFileFilter (C:\Users\carchaw\Documents\pfx_template_generator_api\node_modules\multer\index.js:40:19)
at Busboy.<anonymous> (C:\Users\carchaw\Documents\pfx_template_generator_api\node_modules\multer\lib\make-middleware.js:114:7)
at Busboy.emit (node:events:379:20)
On the form submit, I need upload 2 images from different input field, create new prefix on GCS, and also store the image's name and other's details to be sent in request.body.
From the front-end part, I using Fetch as below:
const getFormContianer = document.getElementById("get_form")
async function handleForm(e) {
e.preventDefault();
let dataForm = new FormData(e.target)
await fetch(file_api, {
method: 'POST',
body: dataForm
}).then((res)=>{
return res.json();
}).then((data)=>{
console.log('api err: '+data);
}).catch((err) =>{
console.log('api err: '+ err)
})
}
getFormContianer.addEventListener('submit', handleForm)
index.html
<form id="get_form">
<label for="video_url">video_url</label>
<input name="video_url" type="text" id="video_url" value=""><br>
<label for="image_backup">image_backup</label>
<input name="image_backup" type="file" id="image_backup" value=""><br>
<label for="image_banner">image_banner</label>
<input name="image_banner" type="file" id="image_banner" value=""><br>
</form>
<input type="submit" id="handle_submit">
Nodejs
multer middleware
const util = require("util");
const multer = require("multer");
let processFile = multer({
storage: multer.memoryStorage()
}).fields([{ name: "image_backup" }, { name: "image_banner" }])
let processFileMiddleware = util.promisify(processFile);
module.exports = processFileMiddleware;
handling Upload
const handleUploadImages = async (req, res) =>{
try {
await processFile(req, res);
if (!req.file) {
return res.status(400).send({ message: "Please upload a file!" });
}
// Create a new blob in the bucket and upload the file data.
const blob = bucket.file(newFolderPath + req.file.originalname);
const blobStream = blob.createWriteStream({
resumable: false,
});
blobStream.on("error", (err) => {
res.status(500).send({ message: err.message });
});
blobStream.on("finish", async (data) => {
// Create URL for directly file access via HTTP.
const publicUrl = format(
`https://storage.googleapis.com/${bucket.name}/${newFolderPath}/${blob.name}`
);
try {
// Make the file public
await bucket.file(newFolderPath + req.file.originalname).makePublic();
} catch {
return res.status(500).send({
message:
`Uploaded the file successfully: ${newFolderPath + req.file.originalname}, but public access is denied!`,
url: publicUrl,
});
}
res.status(200).send({
message: "Uploaded the file successfully: " + newFolderPath + req.file.originalname,
url: publicUrl,
});
});
blobStream.end(req.file.buffer);
} catch (err) {
res.status(500).send({
message: `Could not upload the file: ${req.file.originalname}. ${err}`,
});
}
}
I did use express json and urlencoded on index.js
const express = require('express');
const cors = require('cors');
const config = require('./config')
const app = express()
const templates = require('./routes/templates-routes');
const files = require('./routes/files-routes');
// Middleware
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(express.static('public'))
app.use('/api', templates.routes);
app.use('/create', files.routes);
app.listen(config.port, () => {
console.log(`Example app listening at http://localhost:${config.port}`)
})
Hope that can get some suggestion on this, thank you!
Where is body: dataForm declared? That error arises when you try to upload a field that is not mentioned in fields:
fields([{ name: "image_backup" }, { name: "image_banner" }])
Make sure your multipart form has these 2 fields only for uploading files.
I suggest you to check this post in which they discuss the same issue.
In order to solve it they basically formatted the file that need to be uploaded.
formData.append("type_of_the_file", uploadfile);
Finally solved the issue after few days keep trying.
for front-end POST rest API, passing files by appending file itself and the name.
function handleForm(e) {
e.preventDefault();
let dataForm = new FormData(e.target)
dataForm.append("image_backup", document.getElementById("image_backup").files[0]);
dataForm.append("image_banner", document.getElementById("image_banner").files[0]);
dataForm.append("image_banner_name", document.getElementById("image_banner").value.replace(/^.*[\\\/]/, ''));
dataForm.append("image_backup_name", document.getElementById("image_backup").value.replace(/^.*[\\\/]/, ''));
await fetch(file_api, {
method: 'POST',
body: dataForm
}).then((res)=>{
return res.json();
}).then((data)=>{
console.log('api data: '+ data);
}).catch((err) =>{
console.log('api err: '+ err)
})
}
on Nodejs
const multer = require('multer');
const upload = multer({
storage: multer.MemoryStorage,
}).fields([{name: "image_backup"}, {name: "image_banner"}]);
const startCreatefiles = async(req, res, next) =>{
upload(req, res, (err) => {
console.log(req.body);
console.log(req.files);
})
}
Then successfully get the text form data and file itself.

Multer doesn't return req.body and req.file using next-connect

I'm using nextjs and I want to upload a file so I used next-connect in order to use multer
import nc from "next-connect";
import multer from "multer";
export const config = {
api: {
bodyParser: false,
},
}
const upload = multer({ dest: `${__dirname}../../public` });
const handler = nc()
.use(upload.single('image'))
.post(async (req, res)=>{
console.log(req.body); // undefined
console.log(req.file); // undefined
res.status(200).send("yo");
})
export default handler;
this is the client side code :
function handleOnSubmit(e){
e.preventDefault();
const data = {};
var formData = new FormData(e.target);
for (const [name,value] of formData) {
data[name] = value;
}
data.type = data.type[0].toUpperCase();
data.name = data.name[0].toUpperCase();
axios({
method: "POST",
url:"/api/manager",
data,
config: {
headers: {
'content-type': 'multipart/form-data'
}
}
})
.then(res=>{
console.log(res);
})
.catch(err=>{
throw err
});
}
...
return(
...
<Form onSubmit={(e)=>handleOnSubmit(e)}>
...
</Form>
)
I searched and everything I found was related to nodejs and expressjs but nothing on next.js. I have no idea about how to proceed.
for those who are still looking for a solution on how to use multer as middleware with nextJs, here is an example of an API route, using multer has middleware. The route is calling a getSignedUrl function to upload file to bucket.
running nextJs v12 and multer 1.4.3
import multer from "multer";
import { NextApiRequest, NextApiResponse } from "next";
import { getSignedUrl } from "../../lib/uploadingToBucket";
function runMiddleware(
req: NextApiRequest & { [key: string]: any },
res: NextApiResponse,
fn: (...args: any[]) => void
): Promise<any> {
return new Promise((resolve, reject) => {
fn(req, res, (result: any) => {
if (result instanceof Error) {
return reject(result);
}
return resolve(result);
});
});
}
export const config = {
api: {
bodyParser: false,
},
};
const handler = async (
req: NextApiRequest & { [key: string]: any },
res: NextApiResponse
): Promise<void> => {
//const multerStorage = multer.memoryStorage();
const multerUpload = multer({ dest: "uploads/" });
await runMiddleware(req, res, multerUpload.single("file"));
const file = req.file;
const others = req.body;
const signedUrl = await getSignedUrl(others.bucketName, file.filename);
res.status(200).json({ getSignedUrl: signedUrl });
};
export default handler;
When I switched to the Next.js framework, I first tried to upload a file with the 'multer' library and preferred to give up and use 'formidable'. The reason was that there were a few resources available with Nextjs and multer.
And if I'm not wrong here, multer should be added as middleware, so this means rewriting the server.js page.
You can review these topics:
Create custom server with Nextjs:
https://nextjs.org/docs/advanced-features/custom-server
And adding middleware: https://nextjs.org/docs/api-routes/api-middlewares#connectexpress-middleware-support
If you don't want to deal with this, you can check out a simple gist on using this resource formidable library: https://gist.github.com/agmm/da47a027f3d73870020a5102388dd820
And this is the file upload script I created: https://github.com/fatiiates/rest-w-nextjs/blob/main/src/assets/lib/user/file/upload.ts
had the same problem. don't know if it's the same as yours but in case it might help someone .. when I shifted multer to use memory storage rather than desk storage it worked fine .. in my case i was using another middleware after multer to upload to aws s3 so I didn't need to have the file stored in the ./public permanently .. I just needed the req.file available in the next middleware.
In your case try changing
const upload = multer({ dest: `${__dirname}../../public` });
to
const storage = multer.memoryStorage()
const upload = multer({storage: storage});
It turned out that all you need is to disable NextJS built-in body parser for the specific API route (https://nextjs.org/docs/api-routes/api-middlewares#custom-config):
// The following should be exported from your API route file
export const config = {
api: { bodyParser: false },
};

Fetch /POST request to Server: Empty array when receiving multipart/form-data;

Im trying to upload an image (ReactJs) to my server (NodeJs+ Express+ multerJs) to digital Ocean.
Im using a POST Request with multipart/form-data;
I get a succesfull message from multerJs but I can see in the log on the server that the files: [] has an empty array. And that Digital Ocean didnt add any file of course.
When i do the same Post request with the form without handling the submission of the form ,everything is working and the Files array is not empty.the code above is working :
<form method="post" enctype="multipart/form-data" action="http://localhost:3004/upload_image_test_4" > </form>
Client-Side React.js :
export default class Select_size extends React.Component {
on_form_submit = (e) => {
e.preventDefault();
const fileInput = this.state.file;
const formData = new FormData();
formData.append('file', fileInput);
const options = {
method: 'POST',
body: formData,
headers: {
'Access-Control-Allow-Origin': '*',
Accept: 'application/json',
'Content-Type': 'multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW'
}
};
var url =
this.state.action_link +
'?url=' +
this.state.id_user +
'/' +
this.state.id_draft +
'&name=' +
this.state.name_file;
fetch(url, options);
};
onChange = (e) => {
this.setState({ file: e.target.files[0] });
};
constructor(props) {
super(props);
this.state = {
files: [],
};
this.onChange = this.onChange.bind(this);
}
render() {
return (
<form onSubmit={this.on_form_submit} enctype="multipart/form-data">
<input type="file" name="myImage" onChange={this.onChange} />
<button type="submit">Upload</button>
</form>
)}
}
Server-side (Node.js/ ExpressJs/Multer/Digital Ocean) :
const express = require('express');
const router = express.Router();
const bodyParser = require('body-parser');
const aws = require('aws-sdk');
const spacesEndpoint = new aws.Endpoint('fra1.digitaloceanspaces.com');
const s3 = new aws.S3({
endpoint: spacesEndpoint,
accessKeyId: 'myID',
secretAccessKey: 'MySecretKey'
});
const upload = multer({
storage: multerS3({
s3: s3,
bucket: 'bucketName',
acl: 'public-read',
key: function(request, file, cb) {
cb(null, urlBucket + name_image);
}
})
}).array('upload', 1);
router.post('/upload_image_test_4', function(request, response, next) {
upload(request, response, function(error) {
console.log(request, 'the request');
if (error) {
console.log(error);
return response.json(error, 'error');
}
console.log('File uploaded successfully.');
return response.json('success its uploaded to digital ocean');
});
});
module.exports = router;
If anyone can help me with that ! (I already 'googled' insanely,,,)
Thanks !!
//EDIT I found a solution to my own question... on the github page dedicated to a bug related to Next.js :
The file was not send to the request for some reasons.
I used axios in this way and its now working :
React-js :
sendFile = (e) => {
const data = new FormData();
const file = e.target.files[0];
data.append('avatar', file);
axios
.post('http://localhost:3004/upload_test_stack_2', data)
.then(console.log)
.catch(console.error);
}
The server side (nodeJs) :
const upload = multer({
storage: multerS3({
s3: s3,
bucket: 'My Bucket',
acl: 'public-read',
key: function(request, file, cb) {
cb(null, file.originalname);
}
})
});
router.post('/upload_test_stack_2', upload.single('avatar'), (req, res)
=> {
console.log(req.file, 'The file appears here');
return res.sendStatus(200);
});
Files from input are not an array but FileArray, try [...e.target.files][0]
Related to Event Pooling in React and async behavior of setState.
Modify onChange handler:
Method 1:
Put the file to variable and set it in setState.
onChange = (e) => {
const uploadFile = e.target.files[0] ;
this.setState({ file: uploadFile });
};
Method 2 with event.persist():
onChange = (e) => {
e.persist();
this.setState({ file: e.target.files[0] });
};

vue express uploading multiple files to amazon s3

i need a help on how to upload multiple files image on amazon S3. i let's say that i have three input like this
<template lang="html">
<form enctype="multipart/form-data" #submit.prevent="sendFile">
<input type="file" ref="file1" #change="selectFile1">
<input type="file" ref="file2" #change="selectFile2">
<input type="file" ref="file3" #change="selectFile3">
<div class="field">
<button class="button is-info">Send</button>
</div>
</form>
</template>
and my frontend vue script looks like this
<script>
import _ from 'lodash'
export default {
name: 'BulkUpload',
data() {
return {
file: "",
message: "",
error: false,
sendFiles: []
}
},
methods: {
selectFile1() {
this.file = this.$refs.file1.files[0]
this.sendFiles.push(this.file)
console.log(this.sendFiles);
},
selectFile2() {
this.file = this.$refs.file2.files[0]
this.sendFiles.push(this.file)
console.log(this.sendFiles);
},
selectFile3() {
this.file = this.$refs.file3.files[0]
this.sendFiles.push(this.file)
console.log(this.sendFiles);
},
sendFile() {
let formData = new FormData()
_.forEach(this.uploadFiles, file => {
formData.append('file', file)
})
var self = this
axios.post('http://localhost:3000/image/bulkupload', formData)
.then(function(response) {
console.log(response);
self.message = "File has been uploaded"
self.file = ""
self.error = false
})
.catch(function(err) {
self.message = "Something went wrong"
self.error = true
console.log(err);
})
}
}
}
</script>
after sending it in using formData to the express server, my image route will handle it with this code
const sendingImage = require('../helper/sendingImage');
router.post('/bulkupload', sendingImage.upload.array("file"), (req, res) => {
var promises=[];
for(var i = 0; i < req.files.length; i++) {
var file = req.files[i];
promises.push(sendingImage.uploadLoadToS3(file));
}
Promise.all(promises).then(function(data){
res.send('Uploadedd');
console.log('success');
}).catch(function(err) {
console.log('failed');
res.send(err.stack);
})
})
and my helper looks like this
const multer = require('multer');
const aws = require('aws-sdk');
aws.config.update({
accessKeyId: <my access id>,
secretAccessKey: <my secret key>
})
const upload = multer({
dest: './uploads/'
})
function uploadLoadToS3(ObjFile){
var params = {
ACL :'public-read',
Body : new Buffer(ObjFile.buffer),
Bucket:'vue-express-upload',
ContentType:ObjFile.mimetype,
Key:ObjFile.originalname
}
return s3.upload(params).promise();
}
module.exports = {
upload,
uploadLoadToS3
}
to be honest, my code works without any problem. it send response status 200 without any error.
but the problem is, when i check my S3 bucket, it doesn't have any uploaded file in it. it's still empty.
is there anything wrong with my code ?
The solution I found for using aws-sdk. The good that I will also use this solution and my projects.
Credits: StackOver - Answer
Reference: Multer-s3
In your helper file you'll leave it like this:
const aws = require('aws-sdk')
const multer = require('multer')
const multerS3 = require('multer-s3')
aws.config = new aws.Config({
accessKeyId: <my access id>,
secretAccessKey: <my secret key>,
region: <my region>
})
const s3 = new aws.S3()
const upload = multer({
storage: multerS3({
s3: s3,
acl: 'public-read',
bucket: 'vue-express-upload',
contentType: function(req, file,cb){
cb(null, file.mimetype)
},
key: function (req, file, cb) {
cb(null, file.originalname)
}
})
})
module.exports = upload
The key of the file you can also change to something more unique, like the date / minutes / seconds. It is at your discretion, I used the filename as key.
In your router the route will look like this:
const upload = require('../helper/sendingImage')
router.post('/bulkupload', upload.array('files'), (req, res) => {
console.log('success')
//console.log(req.files)
res.send('Uploadedd')
//res.send(req.files)
})
To view upload data:
router.post('/bulkupload', upload.array('files'), (req, res) => {
res.send({
status: 200,
data: {
sucess: true,
file: req.files
}
})
})
If there is no middleware file, it simply will not send. Will not return error. The only errors that can return are those of AWS access information.
Note:I used the postman.

axios upload to node.js API results in files in aws s3 becoming unreadable

Screen of UploadScreen of DownloadWhenever I utilize the client side upload panel the files that get uploaded to the bucket are named correctly but wont open. They are also a slightly different size from the front end uploaded version. I'm wondering if something in how i'm handling buffers / reading the files is off but I just started working with them yesterday so i'm fairly lost. If any of you based genius programmers could provide some insight into this issue I'd be eternally grateful!!
The overall goal is the ability to upload files to an aws s3 bucket of any file type, then be able to download these files without them being modified or rendered unopenable.
server side javascript;
var express = require("express");
var mongodb = require("mongodb");
var _ = require("lodash");
var bodyParser = require("body-parser");
var app = express();
var router = express.Router();
var mongoose = require("mongoose");
var AWS = require('aws-sdk');
AWS.config.update({region: 'us-west-1'});
var s3 = new AWS.S3({apiVersion: '2006-03-01'});
...
...
...
router.post('/upload', function (req, res) {
var file = new File({name: req.body.fileName, type: req.body.contentType, buffer: req.body.file});
var fs = require('fs');
var fileStream = fs.createWriteStream(req.body.fileName);
fileStream.write(req.body.file)
fileStream.end(function () { console.log('done'); });
fileStream.on('error', function(err) {
console.log('File Error', err);
});
fs.readFile(req.body.fileName, (err, data) => {
if (err) throw err;
console.log(data);
const params = {
Bucket: req.body.crystalName,
Key: req.body.fileName,
Body: data
};
s3.upload(params, function(s3Err, data) {
if (s3Err) {
console.log(s3Err);
} else {
res.send(`File uploaded successfully at ${data.Location}`);
console.log(`File uploaded successfully at ${data.Location}`);
}
});
});
});
client side upload function (vue.js);
<template>
<div class="main">
<input v-model="crystalName" placeholder="Crystal Name" />
<input type="file" #change="onFileChange"/>
<button v-on:click="uploadToCrystal">Upload</button>
<span>{{this.file}}</span>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'crystalviewer',
props: ['user'],
data: function () {
return {
crystalName: '',
fileName: '',
contentType: '',
file: ''
}
},
methods: {
onFileChange (file) {
let vue = this
var reader = new FileReader()
this.fileName = file.target.files[0].name || file.dataTransfer.files[0].name
this.contentType = file.target.files[0].type || file.dataTransfer.files[0].type
console.log(this.contentType)
var start = 0
var stop = file.target.files[0].size
var blob = file.target.files[0].slice(start, stop)
reader.onloadend = function (file) {
vue.file = file.target.result
}
reader.readAsText(blob, 'utf-8')
},
uploadToCrystal () {
let vue = this
axios.post('https://api.mystic-crm.com/crystalviewer/upload', {
crystalName: vue.crystalName,
fileName: vue.fileName,
contentType: vue.contentType,
file: vue.file
})
.then(response => {
console.log(response)
})
.catch(err => {
console.log(err)
})
}
}
}
</script>
<style scoped lang="less">
.main {
}
</style>
To get files after an upload run a get against;
https://api.mystic-crm.com/crystalviewer/contents/:crystalName/:fileName
where;
:crystalName = testcrystalmystic
:fileName = your_file_to_get
Turns out switching to Multiparty was the solution, now I can upload via forms. Apparently axios doesn't support file uploads but forms do so that was a fun revelation. edit added working front end
var express = require("express");
var AWS = require('aws-sdk');
AWS.config.update({region: 'us-west-1'});
var s3 = new AWS.S3({apiVersion: '2006-03-01'});
var multiparty = require('multiparty');
router.post('/upload', function (req, res) {
var form = new multiparty.Form();
var destPath;
var crystalName;
form.on('field', function(name, value) {
if (name === 'crystalName') {
crystalName = value
} else if (name === 'fileName') {
destPath = value;
}
});
form.on('part', function(part) {
s3.putObject({
Bucket: crystalName,
Key: destPath,
ACL: 'public-read',
Body: part,
ContentLength: part.byteCount
}, function(err, data) {
if (err) throw err;
console.log("done", data);
res.end("OK");
console.log("https://s3.amazonaws.com/" + crystalName + '/' + destPath);
});
});
form.parse(req);
});
front end ex;
<template>
<div class="main">
<form v-on:submit="submit">
<input name="crystalName" placeholder="Crystal Name" />
<input name="fileName" v-model="fileName" placeholder="File Name" v-show="false" />
<input name="file" type="file" #change="onFileChange"/>
<input type="submit" value="Upload File" />
</form>
</div>
</template>
<script>
export default {
name: 'crystalviewer',
props: ['user'],
data: function () {
return {
fileName: '',
modal: ''
}
},
methods: {
submit (event) {
let vue = this
var form = document.forms[0]
var request = new XMLHttpRequest()
request.open('POST', 'https://api.mystic-crm.com/crystalviewer/upload', true)
request.setRequestHeader('accept', 'multipart/form-data')
event.preventDefault()
var formData = new FormData(form)
request.send(formData)
request.onreadystatechange = function () {
if (request.readyState < 4) {
vue.modal = 'loading'
} else if (request.readyState === 4) {
if (request.status === 200 || request.status < 300) {
vue.modal = 'success'
console.log('success')
} else {
vue.modal = 'failure'
console.log('failure')
}
}
}
},
onFileChange (file) {
this.fileName = file.target.files[0].name || file.dataTransfer.files[0].name
}
}
}
</script>
<style scoped lang="less">
.main {
}
</style>
React Hooks Version for anyone searching for this
Front End:
import React, {useState} from 'react'
import axios from 'axios'
function Amazonuploadtest() {
const [file, setFile] = useState('')
const submitFile = (e) => {
e.preventDefault();
console.log(file[0])
const formData = new FormData();
formData.append('file', file);
axios.post(`http://localhost:4000/upload`, formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then(response => {
console.log(response)
}).catch(error => {
console.log(error)
});
}
return (
<div>
<form onSubmit={submitFile} enctype="multipart/form-data" style={{paddingTop: 200}}>
<input label='upload file' type='file' name="upl" onChange={e => setFile(e.target.files[0])} />
<button type='submit'>Send</button>
</form>
</div>
);
}
export default Amazonuploadtest
Back End:
require('dotenv').config({ path: __dirname + '/.env' })
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const cors = require('cors');
const path = require('path')
const AWS = require('aws-sdk');
const fs = require('fs');
const multer = require('multer');
const multerS3 = require('multer-s3');
app.use(cors());
app.use(bodyParser.json());
app.use(cookieParser());
// ####################################### ALL AWS STUFF GOES HERE ###############################################################################################
AWS.config.update({
secretAccessKey: 'YOUR KEY GOES HERE',
accessKeyId: 'YOUR KEY GOES HERE',
region: 'YOUR REGION GOES HERE -> You can find it in your bucket URL',
});
const s3 = new AWS.S3();
var upload = multer({
storage: multerS3({
s3: s3,
bucket: 'YOUR BUCKET NAME -> Find it in your Bucket URL',
key: function (req, file, cb) {
cb(null, Date.now().toString() + `.png`); // gives every image upload a unique URL
}
})
});
app.post('/upload', upload.array('file',1), function (req, res, next) {
const key = req.files[0].key
console.log(key)
res.send("Uploaded!");
// We return the key here so that we can then save it as a URL in MongoDB -> allows
// you to call the image URL when rending the image on the front end.
// example -> let avatarURL = `https://s3.amazonaws.com/YOURBUCKETNAME/${key}`
});

Resources