Reputation: 185
Screen of Upload
Screen of Download
Whenever 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
Upvotes: 1
Views: 2062
Reputation: 381
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}`
});
Upvotes: 0
Reputation: 185
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>
Upvotes: 1