I have a problem in my image upload in Laravel Vue. I do not upload image in my project.Here is My Code
<form action="">
<input type="text" id="firstName" class="form-control" v-model="user.firstName" value="Frankie">
<input type="text" id="lastName" class="form-control" v-model="user.lastName" value="Apple">
<select v-model="user.profile.country" v-chosen="user.profile.country" class="option-select">
<option v-for="(country, key) in countries" :value="key">{{country}}</option>
</select>
<input type="file" ref="files" id="imgInp" multiple #change="selectFile">
<input type="button" class="button button__primary button__agree" value="Confirm" #click="submit">
</form>
<script>
export default {
data() {
return {
user : [],
files : [],
uploadFiles : [],
}
},
methods : {
selectFile() {
const files = this.$refs.files.files;
this.uploadFiles = [ ...this.uploadFiles, ...files];
this.files = [
...this.files,
..._.map(files, file => ({
name: file.name,
type: file.type,
size: file.size,
}))
];
},
submit() {
var data = {
'customerDetail': {
'firstName' : this.user.firstName,
'lastName' : this.user.lastName,
'address' : {
'country' : this.user.profile.country,
},
'application': {
'attachments' : this.uploadFiles,
},
},
};
const config = {
headers: { 'content-type': 'multipart/form-data' }
};
axios
.post(`/web/bookings`, data, config)
.then((e) => {
console.log(e);
})
.catch((e) => {
console.log(e);
})
},
},
}
</script>
But When I submit Data it shows error
Missing boundary in multipart/form-data POST data
If I remove config data then my image is not uploaded. I don't get where the problen is. Please help me to solve this problem. Thanks in advance
Related
I'm trying to send file from vue-page to server. For this I use:
FileFrom component:
<template>
<div class="FileForm" v-bind:name="name">
<label class="FileFormLabel">
{{ name }}:
<input type="file" files="files" v-on:change="fileUpload" v-bind:name="name"/>
</label>
<button class="SubmitBtn" v-on:click="submit">
Submit
</button>
</div>
</template>
<script>
export default {
name: 'FileForm',
props: ["value", "name"],
data: function() {
return {
files: [],
}
},
methods: {
fileUpload: function(event) {
this.$emit("upload", [event.target.files[0]])
},
submit: function() {
this.files = []
this.$emit("submit")
},
},
}
</script>
Page:
<template>
<Container>
<div class="content">
<h1>
Upload files
</h1>
<div class="forms">
<FileForm v-bind:files="files['Model']" name="Model" v-on:upload="upload_model" v-on:submit="submit_model">
</FileForm>
</div>
</div>
</Container>
</template>
<script>
import axios from 'axios'
import Container from '#/components/Container.vue'
import FileForm from '#/components/FileForm.vue'
export default {
name: 'App',
data: function() {
return {
files: {},
}
},
components: {
Container,
FileForm
},
methods: {
upload_model: function(file) {
this.files['Model'] = file
},
submit_model: function() {
let formData = new FormData();
formData.append('Model', this.files['Model']);
axios.post('http://' + document.location.host + '/api/upload_model', formData, {
headers: {
'Content-Type': 'multipart/form-data'
},
})
}
},
}
</script>
But sending request looks like this:
-----------------------------316537648527426258472746653245
Content-Disposition: form-data; name="Model"
[object File]
-----------------------------316537648527426258472746653245--
A file is not sent as file but as string "[object File]".
As far as I understand the problem is that FormData.append converts all non-Blob(or File) objects to string and this.files['Model'] is not this one. It is proxy object which Vue.js uses for reactivity.
How can I take the internal object from this proxy and use it for sending?
I changed
formData.append('Model', this.files['Model']);
to
formData.append('Model', this.files['Model'][0]);
in page code.
And now it works fine. I don't know, is it the way to get needed value from proxy or I just returned array of files instead of single file somewhere in my code, but it helped.
Javascript Vue: Where does the variable e in onFileChange(e) originate?
In the following code, there is a variable e in onFileChange(e), where does it originate? It is never declared or imported in the code, so how can it be valid?
Any help would be greatly appreciated.
<template>
<div class="container" style="margin-top: 50px;">
<div class="text-center">
<h4>File Upload with VueJS and Laravel</h4>
<br />
<div style="max-width: 500px; margin: 0 auto;">
<div v-if="success !== ''" class="alert alert-success" role="alert">
{{success}}
</div>
<form #submit="submitForm" enctype="multipart/form-data">
<div class="input-group">
<div class="custom-file">
<input
type="file"
name="filename"
class="custom-file-input"
id="inputFileUpload"
v-on:change="onFileChange"
/>
<label class="custom-file-label" for="inputFileUpload"
>Choose file</label
>
</div>
<div class="input-group-append">
<input type="submit" class="btn btn-primary" value="Upload" />
</div>
</div>
<br />
<p class="text-danger font-weight-bold">{{filename}}</p>
</form>
</div>
</div>
</div>
</template>
<script>
export default {
mounted() {
console.log("Component successfully mounted.");
},
data() {
return {
filename: "",
file: "",
success: ""
};
},
methods: {
onFileChange(e) {
//console.log(e.target.files[0]);
this.filename = "Selected File: " + e.target.files[0].name;
this.file = e.target.files[0];
},
submitForm(e) {
e.preventDefault();
let currentObj = this;
const config = {
headers: {
"content-type": "multipart/form-data",
"X-CSRF-TOKEN": document.querySelector('meta[name="csrf-token"]')
.content
}
};
// form data
let formData = new FormData();
formData.append("file", this.file);
// send upload request
axios
.post("/store_file", formData, config)
.then(function(response) {
currentObj.success = response.data.success;
currentObj.filename = "";
})
.catch(function(error) {
currentObj.output = error;
});
}
}
};
</script>
That declaration is triggered by your template, where you are binding change event to the method. The whole event as parameter gets passed to the method, Refer this section of Vue docs for better information https://v2.vuejs.org/v2/guide/events.html#Method-Event-Handlers
When a variable is called e it is usually the event. You can always console.log(e) and read its properties in the browser console.
But according to this example e is the file that is uploaded:
methods: {
thumbUrl (file) {
return file.myThumbUrlProperty
},
onFileChange (file) {
// Handle files like:
this.fileUploaded = file
}
}
onFileChange(e) has e as event related to the dom. Since while assigning the function in html if there is no parameter passed, the event as a parameter is automatically passed by javaScript.
The declaration onFileChange(e) {
declares a function with the name onFileChange that takes a single parameter e. That is what introduces the variable into the function body.
I am trying to get user input from a form to update onto a webpage. I have recently found out that JSON.stringify does not take html ID elements and that in order to do so they would need to be converted. I have found a guide here but did not help. If anyone has any ideas please let me know, I would truly appreciate it :D
let type = document.getElementById('Type');
let html = type.outerHTML;
let typeData = {
html: html
};
let name = document.getElementById('Name');
let html = name.outerHTML;
let nameData = {
html: html
};
let quantity = document.getElementById('quantity');
let html = quantity.outerHTML;
let quanData = {
html: html
};
update.addEventListener('click', _ => {
fetch('/Plants', {
method: 'put',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
type: typeData,
name: nameData,
quantity: quanData
})
})
.then(res => {
if (res.ok) return res.json()
})
.then(response => {
window.location.reload(true)
})
})
server.js file where all my put route is
app.put('/Plants', (req, res) => {
quoteCollection.findOneAndUpdate(
{ type: req.body.type },
{
$set: {
quantity: req.body.quantity,
type: req.body.type,
name: req.body.name
}
}
)
.then(result => {res.json('Success')})
.catch(error => console.error(error))
});
});
<h2>Update an item</h2>
<form id="inventory-form">
<input type="text" placeholder="Type" name="Type" id="Type" />
<input type="text" placeholder="Name" name="Name" id="Name" />
<input type="number" placeholder="quantity" name="quantity" id="quantity" />
<button id="update-button " type="submit">Submit</button>
</form>
Use formData and do not use the click event but the form submit event:
document.getElementById("inventory-form").addEventListener("submit", function(e) {
e.preventDefault();
const formData = new FormData(this);
fetch('/Plants', {
method: 'put',
body: formData
})
.then(res => {
if (res.ok) return res.json()
})
.then(response => {
// window.location.reload(true) // WHY???
})
})
<h2>Update an item</h2>
<form id="inventory-form">
<input type="text" placeholder="Type" name="Type" id="Type" />
<input type="text" placeholder="Name" name="Name" id="Name" />
<input type="number" placeholder="quantity" name="quantity" id="quantity" />
<button id="update-button" type="submit">Submit</button>
</form>
But WHY reload the page?
Or if you need to refresh WHY use fetch?
I am using ElementUi uploader and i need to send my file with the rest of my form data, but it doesn't seem to send right details of photo to back-end:
Screenshots
Console log when i select an image
Data that sent to back-end
Code
photo input
<el-upload
action="#"
:limit="1"
:multiple="false"
:on-change="photoChanged"
:on-exceed="handleExceed"
list-type="picture-card"
:on-remove="handleRemove"
:on-preview="handlePictureCardPreview"
:before-remove="beforeRemove"
:auto-upload="false">
<i slot="default" class="el-icon-plus"></i>
</el-upload>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="dialogImageUrl" alt="">
</el-dialog>
Script
export default {
data() {
return {
dialogImageUrl: '',
dialogVisible: false,
form: {
name: '',
slug: '',
price: '',
new_price: '',
sku: '',
qty: 1,
active: '',
photo: '',
shortDesc: '',
longDesc: '',
region: '',
date1: '',
date2: '',
type: [],
tags: [],
brand_id: '',
categories: [],
resource: '',
user_id: ''
}
}
},
methods: {
onSubmit(e) { //send data to back-end
e.preventDefault();
axios.post('/api/admin/products/store', this.form)
.then(res => {
console.log(res);
})
.catch(error => {
console.log(error);
})
},
handleRemove(file) {
this.form.photo = ''; // remove photo from from when it's removed
},
photoChanged(file, fileList){
this.form.photo = file.raw; // add photo to form when it's selected
console.log('file', file) // screenshot 1
console.log('raw', file.raw) //screenshot 2
},
handlePictureCardPreview(file) {
this.dialogImageUrl = file.url;
this.dialogVisible = true;
},
handleExceed(files, fileList) {
this.$message.warning(`The limit is 1, you selected ${files.length} files this time, add up to ${files.length + fileList.length} totally, remove old image and try again.`);
},
beforeRemove(file) {
return this.$confirm(`Cancel the transfert of ${ file.name } ?`);
}
},
}
</script>
Any idea?
I have used FormData to send the photo or document to the server.
JavaScript FormData
<form id="myForm" name="myForm">
<div>
<label for="username">Enter name:</label>
<input type="text" id="username" name="username" v-model="imageData.name">
</div>
<div>
<label for="useracc">Enter account number:</label>
<input type="text" id="useracc" name="useracc" v-model="imageData.account">
</div>
<label for="userfile">Upload file:</label>
<input type="file" id="userfile" name="userfile">
</div>
<input type="submit" value="Submit!">
</form>
export default {
data() {
return {
imageData: {}
}
},
methods: {
uploadImageToServer() {
// 1.Save the form Data and return the new id to save the image
axios.post('/api/admin/products/store', this.imageData)
.then(res => {
if(res.id) {
//2. Save the image to id
let formData = new FormData();
let file = document.getElementById('userfile');
formData.append('file', file)
axios.post('/api/admin/products/image/{id}', formData)
.then(res => {
console.log(res)
})
}
})
.catch(err => {
console.log(err)
})
}
}
}
Here,
Both form data & file data maynot be send in single requst.
1. Saving the form data and return the id.
2. Saving the image data to the id.
Replace the html with 'element-ui' syntax. Ensure that your rest api receives the form data as the input.
convert your file to base64
when you select an image, use code below
onImageChange() {
let file = this.form.photo
if (file == '')
return;
this.createImage(file);
}
createImage(file) {
let reader = new FileReader();
let el = this
reader.onload = (e) => {
el.form.photo = e.target.files[0];
};
reader.readAsDataURL(file);
},
attach onImageChange function in your input file
Solved
Well I have decided to give up on sending image with rest of data to backend and upload image first with action="#" in my input and in return i get file name in my form and just send the file name with rest of form instead of sending image file.
<el-upload
action="/api/upload"
:on-success="handleAvatarSuccess"
.....>
methods: {
handleAvatarSuccess(res, file) {
this.form.photo = res.data;
},
}
So it sends my file to back-end as soon as it's selected and set the name of stored file in my form.photo and that name will be send with rest of my form inputs.
Hope it could be useful to others as well.
My html code is
I also need to add sez which is in array format and also i need to add multiple images, need to provide add image and when clicking on it, need to add images as needed by the client
<form method="POST" enctype="multipart/form-data" v-on:submit.prevent="handleSubmit($event);">
<div class="row">
<div class="col-md-4">
<div class="form-group label-floating">
<label class="control-label">Name</label>
<input type="text" class="form-control" v-model="name">
</div>
</div>
<div class="col-md-4">
<div class="form-group label-floating">
<label class="control-label">Alias</label>
<input type="text" class="form-control" v-model="alias">
</div>
</div>
<div class="col-md-4">
<div class="form-group label-floating">
<label class="control-label">Sex</label>
<select class="form-control" v-model="sex" id="level">
<option value="Male">Male</option>
<option value="female">Female</option>
</select>
</div>
</div>
</div>
<div class="row" v-for="(book, index) in sez" :key="index">
<div class="col-md-4">
<div class="form-group label-floating">
<label class="control-label">Date </label>
<input type="date" class="form-control" v-model="book.date">
</div>
</div>
<div class="col-md-8">
<div class="form-group label-floating">
<label class="control-label"> Details</label>
<input type="text" class="form-control" book.details>
</div>
</div>
</div>
<a #click="addNewRow">Add</a>
<div class="card-content">
<div class="row">
<div class="col-md-4">
<div class="button success expand radius">
<span id="save_image_titlebar_logo_live">Signature</span>
<label class="custom-file-upload"><input type="file" name="photo" accept="image/*" />
</label>
</div>
</div>
<div class="col-md-4">
<div class="button success expand radius">
<span id="save_image_titlebar_logo_live">Recent Photograph</span>
<label class="custom-file-upload">
<input type="file" name="sign"/>
</label>
</div>
</div>
</div>
</div>
</form>
My vue js code is
addForm = new Vue({
el: "#addForm",
data: {
name: '',
alias: '',
sex: '',
sez: [{
date: null,
details: null,
}, ],
photo: '',
sign: '',
},
methods: {
addNewRow: function() {
this.seziure.push({
date: null,
details: null,
});
},
handleSubmit: function(e) {
var vm = this;
data = {};
data['sez'] = this.sez;
data['name'] = this.name;
data['alias'] = this.alias;
data['sex'] = this.sex;
//how to add images
$.ajax({
url: 'http://localhost:4000/save/',
data: data,
type: 'POST',
dataType: 'json',
success: function(e) {
if (e.status) {
vm.response = e;
alert("success")
} else {
vm.response = e;
console.log(vm.response);
alert("Registration Failed")
}
}
});
return false;
},
},
});
This is my code. I have no idea about how to add images in this case.
Can anyone please help me pass this data.
How to pass this data along with images to the backend?
I don't want to use base64 encoding. I need to just pass this image in this ajax post request along with other data
Using axios:
Template
...
<input type="file" name="photo" accept="image/*" #change="setPhotoFiles($event.target.name, $event.target.files) />
...
Code
data () {
return {
...
photoFiles: [],
...
}
},
...
methods: {
...
setPhotoFiles (fieldName, fileList) {
this.photoFiles = fileList;
},
...
handleSubmit (e) {
const formData = new FormData();
formData.append('name', this.name);
formData.append('alias', this.alias);
formData.append('sex', this.sex);
...
this.photoFiles.forEach((element, index, array) => {
formData.append('photo-' + index, element);
});
axios.post("http://localhost:4000/save/", formData)
.then(function (result) {
console.log(result);
...
}, function (error) {
console.log(error);
...
});
}
}
I'm not sure where would you like the extra images to appear, but I added them after this column:
<div class="col-md-4">
<div class="button success expand radius">
<span id="save_image_titlebar_logo_live">Recent Photograph</span>
<label class="custom-file-upload">
<input type="file" name="sign"/>
</label>
</div>
</div>
And here's the column I added — "add images": (You can try this feature here, with the updates)
<div class="col-md-4">
<ul class="list-group" :if="images.length">
<li class="list-group-item" v-for="(f, index) in images" :key="index">
<button class="close" #click.prevent="removeImage(index, $event)">×</button>
<div class="button success expand radius">
<label class="custom-file-upload">
<input type="file" class="images[]" accept="image/*" #change="previewImage(index, $event)">
</label>
</div>
<div :class="'images[' + index + ']-preview image-preview'"></div>
</li>
</ul>
<button class="btn btn-link add-image" #click.prevent="addNewImage">Add Image</button>
</div>
And the full Vue JS code (with jQuery.ajax()):
addForm = new Vue({
el: "#addForm",
data: {
name: '',
alias: '',
sex: '',
sez: [{
date: null,
details: null
}],
// I removed `photo` and `sign` because (I think) the're not necessary.
// Add I added `images` so that we could easily add new images via Vue.
images: [],
maxImages: 5,
// Selector for the "Add Image" button. Try using (or you should use) ID
// instead; e.g. `button#add-image`. But it has to be a `button` element.
addImage: 'button.add-image'
},
methods: {
addNewRow: function() {
// I changed to `this.sez.push` because `this.seziure` is `undefined`.
this.sez.push({
date: null,
details: null
});
},
addNewImage: function(e) {
var n = this.maxImages || -1;
if (n && this.images.length < n) {
this.images.push('');
}
this.checkImages();
},
removeImage: function(index) {
this.images.splice(index, 1);
this.checkImages();
},
checkImages: function() {
var n = this.maxImages || -1;
if (n && this.images.length >= n) {
$(this.addImage, this.el).prop('disabled', true); // Disables the button.
} else {
$(this.addImage, this.el).prop('disabled', false); // Enables the button.
}
},
previewImage: function(index, e) {
var r = new FileReader(),
f = e.target.files[0];
r.addEventListener('load', function() {
$('[class~="images[' + index + ']-preview"]', this.el).html(
'<img src="' + r.result + '" class="thumbnail img-responsive">'
);
}, false);
if (f) {
r.readAsDataURL(f);
}
},
handleSubmit: function(e) {
var vm = this;
var data = new FormData(e.target);
data.append('sez', this.sez);
data.append('name', this.name);
data.append('alias', this.alias);
data.append('sex', this.sex);
// The `data` already contain the Signature and Recent Photograph images.
// Here we add the extra images as an array.
$('[class~="images[]"]', this.el).each(function(i) {
if (i > vm.maxImages - 1) {
return; // Max images reached.
}
data.append('images[' + i + ']', this.files[0]);
});
$.ajax({
url: 'http://localhost:4000/save/',
data: data,
type: 'POST',
dataType: 'json',
success: function(e) {
if (e.status) {
vm.response = e;
alert("success");
} else {
vm.response = e;
console.log(vm.response);
alert("Registration Failed");
}
},
cache: false,
contentType: false,
processData: false
});
return false;
},
},
});
Additional Notes
I know you're using Node.js in the back-end; however, I should mention that in PHP, the $_FILES variable would contain all the images (so long as the fields name are properly set); and I suppose Node.js has a similar variable or way of getting the files.
And in the following input, you may have forgotten to wrap book.details in v-model:
<input type="text" class="form-control" book.details>
<input type="text" class="form-control" v-model="book.details"> <!-- Correct -->
UPDATE
Added feature to limit number of images allowed to be selected/uploaded, and added preview for selected image. Plus the "send images as array" fix.
If you're using HTML5, try with a FormData object ; it will encode your file input content :
var myForm = document.getElementById('addForm');
formData = new FormData(myForm);
data: formData
Use below templete to show/upload the image:
<div v-if="!image">
<h2>Select an image</h2>
<input type="file" #change="onImageUpload">
</div>
<div v-else>
<img :src="image" />
<button #click="removeImage">Remove image</button>
</div>
Js code:
data: {
image: '',
imageBuff: ''
},
methods: {
onImageUpload(e) {
var files = e.target.files || e.dataTransfer.files;
if (!files.length)
return;
this.createImage(files[0]);
},
createImage(file) {
var image = new Image();
var reader = new FileReader();
this.imageBuff = file;
reader.onload = (e) => {
this.image = e.target.result;
};
reader.readAsDataURL(file);
},
removeImage: function(e) {
this.image = '';
},
handleSubmit(e) {
const formData = new FormData();
formData.append('name', this.name);
formData.append('alias', this.alias);
formData.append('sex', this.sex);
formData.append('image', this.imageBuff);
...
// Ajax or Axios can be used
$.ajax({
url: 'http://localhost:4000/save/',
data: formData,
processData: false, // prevent jQuery from automatically transforming the data into a query string.
contentType: false,
type: 'POST',
success: function(data) {
console.log(data);
...
}
});
}
}