When I want to upload an image and send the other data using FormData. Axios seems to serialize the image. Therefore, when i upload the image using Axios, the image is inside the body's payload as a string. As a result, I'm unable to use Multer on the server side to retrieve the uploaded image from the request.
This is my Vue code:
export default () {
name: 'app',
data () {
image: ''
},
methods: {
onFileChange (e) {
var files = e.target.files || e.dataTransfer.files
if (!files.length) {
return
}
// console.log(files[0])
// var x = files[0]
return this.createImage(files[0])
// return new Buffer(x)
},
createImage (file) {
// var image = new Image()
var reader = new FileReader()
var vm = this
reader.onload = (e) => {
// vm.image = e.target.result
vm.image = file.toString('base64')
console.log(vm.image)
}
reader.readAsDataURL(file)
},
submitStaff () {
const formData = new FormData()
formData.append('file', this.image)
formData.append('name', this.data.name)
formData.append('username', this.data.username)
formData.append('password', this.data.password)
axios.post('api/myApiUrl', formData)
.then(function (response) {
console.log(response)
})
.catch(function (error) {
console.log(error)
})
}
}
<input type="file" #change="onFileChange">
Request Payload (Error with Vue)
Request Payload (Successful with Postman)
How do i fix this? Thank you!
I faced a problem related to it and in my case I was setting Content-Type as a default configuration for all my requests, doing this:
axios.defaults.headers.common['Content-Type'] = 'application/json [or something]';
And, as I remember, part of my solution was to remove this configuration to make the upload works. I just erased that line from my code and all requests worked well. As soon I removed it, I could notice that my headers request changed to this (see attached image and notice the Content-Type entry):
Another thing you should be alert is the field name to your <input type="file" name="file">. I have a java backend expecting a parameter named as "file", so, in my case, my input file HAS to be set as "file" on my HTML and on my server side, like this:
public ResponseEntity<Show> create(
#RequestParam("file") MultipartFile file,
RedirectAttributes redirectAttributes) { ... }
Observe the entry #RequestParam("file")...
And in the HTML file:
<form enctype="multipart/form-data" novalidate v-if="isInitial || isSaving">
<div class="well">
<div class="form-group">
<label>* Selecione o arquivo excel. O sistema fará o upload automaticamente!</label>
<div class="dropbox">
<input type="file" single name="file" :disabled="isSaving" #change="filesChange($event.target.name, $event.target.files); fileCount = $event.target.files.length"
accept="application/vnd.ms-excel" class="input-file">
<p v-if="isInitial">
Solte o arquivo excel aqui<br> ou clique para buscar.
</p>
<p v-if="isSaving">
Carregando arquivo...
</p>
</div>
</div>
</div>
</form>
You also can take a look at this tutorial, it my help you: https://scotch.io/tutorials/how-to-handle-file-uploads-in-vue-2
I hope it helps you!
Related
please I'm lost how can I upload multiple files
This is how I upload single files:
<div class="a-row a-spacing-top-medium">
<label class="choosefile-button">
<i class="fal fa-plus"></i>
<input
type="file"
#change="onFileSelected"
ref="files"
/>
<p style="margin-top: -70px">{{ fileName }}</p>
</label>
</div>
this is my script tag for image upload:
<script>
import axios from 'axios'
export default {
data() {
return {
selectedFile: null,
fileName: '',
photos: null
}
},
methods: {
onFileSelected(event) {
this.selectedFile = event.target.files[0]
console.log(this.selectedFile)
this.fileName = event.target.files[0].name
},
async onAddProduct() {
let data = new FormData()
data.append('photos', this.selectedFile, this.selectedFile.name)
const response = await axios
.post('http://localhost:5000/api/products', data)
.then(() => {
console.log(response)
})
}
}
}
</script>
each time I add multiple to my input tag it just uploads an image in my browser.
Please how can I go about multiple uploads?
Here's a basic blog post on how to upload a files to S3 in Vue through a node backend. This may help you out.
Basically what you need to do is create a backend that handles the files and uploads them. This can be done using multer, multer-s3 and node.
One thing that you should also change is just having
data.append('photos', this.selectedFile)
data.append('fileName', this.selectedFile.name)
I'm using react-image-picker to take pictures and get the URI but I'm having problems receiving images as an IFormFile in my API. When using the "Content-Type": "multipart/form-data" header my array is null. Without it the array is empty but the data is there as a key-value pair the value is of type object inside HttpContext.Request.Form.
I've tried using both axios and fetch. I've tried sending the object as a blob with the content-type specified as image/jpeg, I've tried stringifying the Asset object but my request doesn't show a content-type for the FormData no matter what.
I tried creating a quick Vue project and the request goes through to my API as a file there without any issues.
It seems like the difference comes down to whether the FormData has a content-type specified. on Vue, it looks like
------WebKitFormBoundaryQoBY2Xgnc8K2sTzQ
Content-Disposition: form-data; name="photos"; filename="2560px-Random_Turtle.jpg"
Content-Type: image/jpeg
while in react it looks like. It looks like it even ignores the filename that's set in the append.
------WebKitFormBoundaryQoBY2Xgnc8K2sTzQ
Content-Disposition: form-data; name="photos"
Is there any way to force the content-type in the FormData? Using Blob like this doesn't change the request in any way
data.append("photos", new Blob([JSON.stringify(photo)], {type: "image/jpeg"}), "2560px-Random_Turtle")
React Native (Not working)
export const TestWithPhotosAPI = async (photos: Asset[]) => {
let data = new FormData();
if (containerAndPhotos.photos) {
containerAndPhotos.photos.forEach((photo) => {
data.append("photos", photo as Blob, "2560px-Random_Turtle");
});
}
fetch("https://myapi/postpicture", {
method: "POST",
body: data,
});
};
Vue project (Works)
<script setup lang="ts">
import { ref } from 'vue'
const form = ref(null)
const files = ref<FormData>(new FormData)
const filesChange = (event ) => {
console.log(event.target.files);
Array.from(event.target.files).map((file, index) => {
console.log(file);
files.value.append("photos", file as Blob, event.target.files[index].name)
})
}
const upload = () => {
fetch(
'https://myapi/postpicture',
{
method: "POST",
body: files.value
}
);
}
</script>
<template>
<form ref="form" #submit.prevent="upload">
<input type="file" name="file" multiple #change="(event) => filesChange(event)" />
<input type="submit" />
</form>
</template>
API (Works with Vue)
[HttpPost]
public ActionResult SetAsDone([FromForm] ICollection<IFormFile> photos)
{
var httpRequest = HttpContext.Request.Form;
return Ok();
}
i'm writing a upload page using filepond. The problem i'm facing is to get it to work like i want. I need to get the metadata from the input and post it with additional formdata
I can allready post the image and additional formdata. However I also want the metadata from the input. If I use onaddfile and log the metadata it works and shows additional="1"
<input type="file" id="img" name="img" data-file-metadata-additional="1">
pond=FilePond.create(document.querySelector('#img'),{
server: {
process: {
url: 'opbouw/model/post/addimg.php',
ondata: (formData) => {
// > POST META DATA
formData.append('userid', '2');
formData.append('usercom', '1');
return formData;
}
}
},
});
pond.onaddfile = (err, item) => {
console.log(item);
const metadata = item.getMetadata();
console.log(metadata);
}
So no clue how to post the metadata
If you're not going to write a custom server.process method then you're going to have to "remember" which metadata belongs to which file.
Something like this:
<input type="file" id="img" name="img" data-file-metadata-additional="1">
<script>
let pondMetadata;
const pond = FilePond.create(document.querySelector('#img'), {
server: {
process: {
url: 'opbouw/model/post/addimg.php',
ondata: (formData) => {
// add metadata
formData.append('usermeta', JSON.stringify(pondMetadata));
formData.append('userid', '2');
formData.append('usercom', '1');
return formData;
}
}
},
onaddfile: (err, item) => {
pondMetadata = item.getMetadata();
}
});
</script>
I am trying to upload images to realtime firebase database. I made it with one images but I have no idea how to add multiple images.
This is my solution for one image.
<v-layout row>
<v-flex xs12 sm6 offset-sm3>
<!-- accept nam govori da uzme slike i nista vise-->
<v-btn raised #click="onPickFile">Upload image</v-btn>
<input
type="file"
style="display:none"
ref="fileInput"
accept="image/*"
#change="onFilePicked"/></v-flex
></v-layout>
In data I have this two: imgURL: "",
image: null
And this is a method:
onFilePicked(event) {
//files is list of imaages by puting files[0] we are taking just one
const files = event.target.files;
let filename = files[0].name;
if (filename.lastIndexOf(".") <= 0) {
return alert("Please add a valid file!");
}
const fileReader = new FileReader();
fileReader.addEventListener("load", () => {
this.imgURL = fileReader.result;
});
fileReader.readAsDataURL(files[0]);
this.image = files[0];
},
Although, this is not a firebase related question, but I try to help anyway:
First of all, you should add multiple to the input-tag, so you can select more than only one file there:
// CHANGE THIS:
<input type="file" style="display:none" ref="fileInput" accept="image/*" #change="onFilePicked"/>
// TO THIS:
<input type="file" style="display:none" ref="fileInput" accept="image/*" #change="onFilePicked" multiple/>
I assume you stumbled upon this wierd 'for-loop' for the files.. I had the same issue and figured out when I type of instead of in, then I get the file itself, not just the index and there is no error.
I splitted up the main parts of your function and converted them in asyncron Promises (because there might be a timing error with the addEventListener)
This is just the idea of the multiple files upload. Maybe you should add some more error-catcher:
new Vue({
data: {
blobs: [],
images: [],
},
methods: {
async onFilePicked(event) {
const files = event.target.files
this.images =[]
this.blobs =[]
for (let file of files) {
this.images.push(file)
let blob = await this.prepareImagesForUpload(file)
this.blobs.push(blob)
}
this.uploadImagesToFirebase()
},
prepareImagesForUpload(file) {
return new Promise((resolve, reject) => {
let filename = file.name
if (filename.lastIndexOf(".") <= 0) {
alert("Please add a valid file: ", filename)
reject()
}
const fileReader = new FileReader()
fileReader.readAsDataURL(file)
fileReader.addEventListener("load", async () => {resolve(fileReader.result)})
})
},
uploadImagesToFirebase() {
//... do the firebase upload magic with:
console.log(this.blobs)
//...
}
}
})
I am trying to upload a file to the server with vuejs, actually i don't want the form to treat the file and upload it, i prevent the submit and treat some logic by myself, so at the begin i want to do something simple just check if it is a pdf and if everything is ok it should point to /upload in the localhost defined for NodeJS server
<template>
<div id="app">
<form>
<h2>Select a file</h2>
<input id="inputVal" type="file" #change="onFileChange">
<button #click.prevent="sendFile()">Send</button>
</form>
</div>
</template>
<!-- Javascript -->
<script>
export default {
name: 'app',
data() {
return {
file: ''
}
},
methods: {
onFileChange(e) {
var files = e.target.files || e.dataTransfer.files;
if (!files.length) {
return;
}
var ext = files[0].name.split('.').pop();
if(ext === 'pdf') {
this.createFile(files[0]);
}
else {
this.file = '';
this.clearFileInput(document.getElementById('inputVal'));
}
},
createFile(file) {
this.file = file;
},
clearFileInput(ctrl) {
try {
ctrl.value = null;
} catch(ex) { }
if (ctrl.value) {
ctrl.parentNode.replaceChild(ctrl.cloneNode(true), ctrl);
}
},
sendFile() {
var vm = this;
if(this.file !== '') {
this.$http.post('http://localhost:3000/upload',{params: vm.file, headers: {'Content-Type': 'multipart/form-data'}})
.then(response => {
// JSON responses are automatically parsed.
//
}, error => {
//this.errors.push(e)
});
}
}
}
}
</script>
on my server side i am receiving undefined, i don't really know why, i just wanna see if i really got any file in the server, and if yes i want to save it on the server side, any help with that?
i just did this on the /upload endpoint:
router.post('/upload',function(req,res) {
console.log(res.body);
})
You will get Files object from input which is not the actual file but a special reference to a file, you need to useFileReader or FormData to get the actual file and send it, try something like this
var data = new FormData();
var pdf = e.target.files[0];
data.append('file',pdf)
this.$http.post('http://localhost:3000/upload',{params: data}, headers: {'Content-Type': 'multipart/form-data'}})