Upload Image in Vue Component with Laravel - laravel-5

I am making a simple website that has a feature to upload images. I tried it Laravel way which I made it in blade template and it works fine. Now I am trying to make it inside Vue Components
Here's my Create.vue
<template>
<div>
<div class="row">
<input type="hidden" name="_token" :value="csrf">
<div class="col-md-5">
<div class="detail-container">
<label for="title">Book Title:</label>
<input type="text" name="title" id="title" v-model="book_title" class="form-control">
</div>
<div class="detail-container">
<label for="title">Book Description:</label>
<textarea type="text" name="description" id="description" v-model="book_description" class="form-control" rows="5"></textarea>
</div>
<div class="detail-container">
<label for="title">Tags:</label>
<multiselect v-model="tags" :show-labels="false" name="selected_tags" :hide-selected="true" tag-placeholder="Add this as new tag" placeholder="Search or add a tag" label="name" track-by="id" :options="tagsObject" :multiple="true" :taggable="true" #tag="addTag" #input="selectTags">
<template slot="selection" slot-scope="tags"></template>
</multiselect>
</div>
</div>
<div class="col-md-7">
<!-- BOOK COVER WILL GO HERE -->
<div class="detail-container">
<label>Book Cover:</label>
<input type="file" class="form-control-file" id="book_cover" name="selected_cover" #change="onFileChange">
<small id="fileHelp" class="form-text text-muted">After you select your desired cover, it will show the preview of the photo below.</small>
<div id="preview">
<img v-if="url" :src="url" height="281" width="180" />
</div>
</div>
</div>
<div class="detail-container" style="margin-top: 20px;">
<button class="btn btn-primary" #click="saveBook()">Next</button>
</div>
</div>
</div>
</template>
<script>
import Multiselect from 'vue-multiselect'
// register globally
Vue.component('multiselect', Multiselect)
export default {
// OR register locally
components: { Multiselect },
data () {
return {
csrf: document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
url: null,
selected_cover: null,
tags: [],
tagsObject: [],
selected_tags: [],
book_title: '',
book_description: ''
}
},
methods: {
getTags() {
let vm = this;
axios.get('/admin/getTags').then(function(result){
let data = result.data;
for(let i in data) {
vm.tagsObject.push({id: data[i].id, name: data[i].name});
}
});
},
addTag (newTag) {
const tag = {
name: newTag,
id: newTag.substring(0, 2) + Math.floor((Math.random() * 10000000))
}
this.tagsObject.push(tag);
this.tags.push(tag);
},
selectTags(value) {
this.selected_tags = value.map(a=>a.id);
},
onFileChange(e) {
const file = e.target.files[0];
this.url = URL.createObjectURL(file);
this.selected_cover = file;
},
saveBook() {
const fd = new FormData();
fd.append('image', this.selected_cover, this.selected_cover.name)
console.log(this.selected_cover);
var book_details = {
'title': this.book_title,
'description': this.book_description,
'book_cover': this.selected_cover,
'tags': this.selected_tags
};
axios.post('/admin/saveBook', book_details).then(function(result){
console.log('done')
})
}
},
created() {
this.getTags();
}
}
</script>
<!-- New step!
Add Multiselect CSS. Can be added as a static asset or inside a component. -->
<style src="vue-multiselect/dist/vue-multiselect.min.css"></style>
and here's my controller
public function store(Request $request)
{
$this->validate(request(), [
'title' => 'required|min:5',
'description' => 'required|min:10',
'book_cover' => 'required|image|mimes:jpeg,jpg,png|max:10000'
]);
// File Upload
if($request->hasFile('book_cover')) {
$fileNameWithExt = $request->file('book_cover')->getClientOriginalName();
// GET FILE NAME
$filename = pathinfo($fileNameWithExt, PATHINFO_FILENAME);
// GET EXTENSION
$extension = $request->file('book_cover')->getClientOriginalExtension();
// File Unique Name
$fileNameToStore = $filename. '_'. time().'.'.$extension;
$path = $request->file('book_cover')->storeAs('public/book_covers', $fileNameToStore);
} else {
$fileNameToStore = 'noimage.jpg';
}
$book = new Book;
$book->title = request('title');
$book->description = request('description');
$book->book_cover = $fileNameToStore;
$book->save();
$book->tags()->sync($request->tags, false);
return back()->with('success', 'Book Created Successfully!');
}
I never touched my controller because this is what I used when I do this feature in Laravel Way but when I save it, the details are being saved but the image is not uploading instead it saves noimage.jpg in the database. Does anyone know what I am doing wrong?
i tried to add this part const fd = new FormData(); in book_details but when i console.log(fd) it returned no data.

you should pass the fd object and not the book_details in your POST request.
you could do it something like this.
onFileChange(e) {
const file = e.target.files[0];
// this.url = URL.createObjectURL(file);
this.selected_cover = file;
},
saveBook() {
const fd = new FormData();
fd.append('image', this.selected_cover)
fd.append('title', this.book_title)
fd.append('description', this.book_description)
fd.append('book_cover', URL.createObjectURL(this.selected_cover))
fd.append('tags', this.selected_tags)
axios.post('/admin/saveBook', fd).then(function(result){
console.log('done')
})
}
and also, you can't just console.log the fd in the console. what you can do instead is something like this
for (var pair of fd.entries()) {
console.log(pair[0]+ ', ' + pair[1]);
}
FormData is a special type of object which is not stringifyable and cannot just be printed out using console.log. (link)

Related

How to Upload pdf, xls, xlsx, doc, docx in Laravel and Vue.js

My Code is below but it's not working. I can upload Image with base64 formate and successfully store on Laravel backend but I'm struggling to upload other file formates in Laravel and vue.js. Anyone can help please?
.vue file
<template>
<div>
<form enctype="multipart/form-data" #submit.prevent="handleSubmit">
<div class="form-group mb-4">
<label>Institute Prospectus</label>
<label class="custom-file-container__custom-file">
<input
type="file"
class="form-control-file"
#change="onChangeProspectus"
/>
</label>
</div>
</form>
</div>
</template>
<script>
export default {
data() {
return {
institute_prospectus: "",
};
},
methods: {
handleSubmit() {
let data = {
institute_prospectus: this.institute_prospectus,
};
axios.post(data, "/add-institute")
.then(() => {
console.log("Success");
})
.catch((err) => {
console.log(err);
});
},
onChangeProspectus(event) {
let file = event.target.files[0];
this.institute_prospectus = file.name;
},
},
};
</script>
InstituteController.php
public function add_institute(Request $request) {
$prospectus_file = $request->institute_prospectus;
if ($prospectus_file) {
$ext = substr($prospectus_file, strrpos($prospectus_file, '.', -1), strlen($prospectus_file));
$name = time().'-'.$slug.".".$ext;
$upload_path = 'backend/files/institute/';
$prospectus_url = $upload_path.$name;
$prospectus_file->move($upload_path,$prospectus_url);
}
}

Capturing multiple image in Vue Js

I have a problem capturing multiple images to my function in vue and pass to the controller. I am trying to modify my blade file which works well when using just the normal form. I am trying to modify to vue but I have a problem with image section. Please assist me on how to achieve this.
my form:
<label for="">Description</label>
<textarea name="description" class="form-control" v-model="description"> </textarea>
<label for="">Images</label>
<input type="file" #change="fieldChange" class="form-control input-sm" name="images[]"
multiple>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
The vue section
data(){
return {
id:'',
price:'',
title:'',
description:'',
location:'',
images:[],
}
},
methods:{
fieldChange(e){
let selectedFiles=e.target.files;
if(!selectedFiles.length){
return false;
}
for(let i=0;i<selectedFiles.length;i++){
this.images.push(selectedFiles[i]);// am able to console the images
}
},
saveImageData(){
var self=this;
axios.post('/senddata',{
title:this.title,
description:this.description,
location:this.location,
images:this.images.file.name,
price:this.price,
})
},
My Laravel function
public function store(Request $request)
{
$product=Products::create([
'title'=>$request['title'],
'description'=>$request['description'],
'price'=>$request['price'],
'location'=>$request['location']
]);
$images= $request->file('images');
foreach ($images as $image){
$move=$image->move(public_path().'/images2/',$image->getClientOriginalName());
if($move){
$imagedata=Images::create([
'title'=>$image->getClientOriginalName(),
'filename'=>$image->getClientOriginalName()
]);
$product->images()->attach([$imagedata->id]);
}
Here is the working code for vuejs part - https://picupload.netlify.app/
https://github.com/manojkmishra/dw_take5/blob/master/src/components/ImageUploader.vue
Vue part
<template>
<div class="uploader"
#dragenter="OnDragEnter"
#dragleave="OnDragLeave"
#dragover.prevent
#drop="onDrop"
:class="{ dragging: isDragging }">
<div class="upload-control" v-show="images.length">
<label for="file">Select files</label>
<button #click="upload">Upload</button>
</div>
<div v-show="!images.length">
<i class="fa fa-cloud-upload"></i>
<p>Drag your images here</p><div>OR</div>
<div class="file-input">
<label for="file">Select files</label>
<input type="file" id="file" #change="onInputChange" multiple>
</div>
</div>
<div class="images-preview" v-show="images.length">
<div class="img-wrapper" v-for="(image, index) in images" :key="index">
<img :src="image" :alt="`Image Uplaoder ${index}`">
<div class="details">
<span class="name" v-text="files[index].name"></span>
<span class="size" v-text="getFileSize(files[index].size)"> </span>
</div>
</div>
</div>
<div v-show="images.length" class="progress">
<div
class="progress-bar progress-bar-info progress-bar-striped"
role="progressbar" :aria-valuenow="progress"
aria-valuemin="0" aria-valuemax="100"
:style="{ width: progress + '%' }"
>
{{ progress}}%
</div>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default
{ data: () => ({ isDragging: false, dragCount: 0, files: [],images: [] ,progress:0}),
methods: {
OnDragEnter(e) { e.preventDefault();
this.dragCount++;
this.isDragging = true;
return false;
},
OnDragLeave(e) { e.preventDefault();
this.dragCount--;
if (this.dragCount <= 0) this.isDragging = false;
},
onInputChange(e) { const files = e.target.files;
Array.from(files).forEach(file => this.addImage(file));
},
onDrop(e) {console.log('ondrop-evnt e=',e)
e.preventDefault();
e.stopPropagation();
this.isDragging = false;
const files = e.dataTransfer.files;
Array.from(files).forEach(file => this.addImage(file));
},
addImage(file) {console.log('addimage file=',file)
if (!file.type.match('image.*')) { this.$toastr.e(`${file.name} is not an image`);
return;
}
this.files.push(file);
const img = new Image(),
reader = new FileReader();
reader.onload = (e) => this.images.push(e.target.result);
reader.readAsDataURL(file);
console.log('addimage this.images=',this.images)
},
getFileSize(size) { const fSExt = ['Bytes', 'KB', 'MB', 'GB'];
let i = 0;
while(size > 900) { size /= 1024; i++; }
return `${(Math.round(size * 100) / 100)} ${fSExt[i]}`;
},
upload() { //this.progress = '0';
const formData = new FormData();
this.files.forEach(file =>
{ formData.append('images[]', file, file.name); });
console.log('upload triggered FormData=',formData)
// resp=axios.post('http://127.0.0.1:8000/sendemail1',this.formData);
// axios.post('http://127.0.0.1:8000/api/imagesupload', formData,
axios.post('https://uat.oms.dowell.com.au/api/imagesupload', formData,
{onUploadProgress:uploadEvent=>{ this.progress=Math.round(uploadEvent.loaded/uploadEvent.total*100);
console.log('upld prges:'+ Math.round(uploadEvent.loaded/uploadEvent.total*100)+'%')
}
})
//axios.post('https://uat.oms.dowell.com.au/api/imagesupload', formData)
.then(response => {
this.$toastr.s('All images uplaoded successfully');
this.images = [];
this.files = [];
this.progress = 0;
})
.catch(() => {
this.$toastr.e('Could not upload the files!');
this.images = [];
this.files = [];
this.progress = 0;
});
}
}
}
</script>
Another version with vuetify
https://github.com/manojkmishra/take5-front-admin/blob/master/src/components/img/imgup.vue
Here is laravel part
public function imagesupload(Request $request){
if (count($request->images)) {
foreach ($request->images as $image) {
$image->store('images');
}
}
return response()->json(["message" => "Done"]);
}
Another way in laravel is here
https://github.com/manojkmishra/take5-api/blob/master/app/Http/Controllers/PicController.php

Image required validation using vee-validate vue

I'm new to vueJS.
I want to add a required validator in image using vee-validate.
Built-in required validador isn't working so I created a custom validator img_required.
here's what I've done so far.
.vue html part
<ValidationProvider rules="image|img_required" bail="false" v-slot="{ errors, validate }">
<div class="row row-xs mg-t-20 mx-0">
<label class="col-sm-4 form-control-label">
<span class="tx-danger">*</span> Image:
</label>
<div
#dragover.prevent
#change="validate"
#drop.prevent
class="file-wrapper col-sm-8 mg-t-10 mg-sm-t-0"
>
<div v-if="imgUrl">
<button #click="imageNull" class="img-close">
<b-icon icon="x"></b-icon>
</button>
<img style="height: 127px" :src="imgUrl" />
</div>
<input type="text" hidden v-model="imgUrl" />
<div v-if="!imgUrl" #drop="handleImage($event, 'drop')">
<input
type="file"
class="form-control"
name="file"
accept="image/*"
#change="handleImage($event, 'input'); validate()"
/>
Drop image
</div>
</div>
</div>
<div v-for="error in errors" :key="error">{{ error }}</div>
</ValidationProvider>
I can't use v-model in input type file so I created a dummy hidden input field and passed imgUrl in v-model <input type="text" hidden v-model="imgUrl" />
imgUrl gets image src from file drop or input file.
I added a close button to nullify imgUrl variable.
<button #click="imageNull" class="img-close">
<b-icon icon="x"></b-icon>
</button>
I pass this imgUrl to vee-validate extend method.
.vue script part
data() {
return {
imgUrl: ""
}
},
methods: {
handleImage(e, action) {
var file;
if (action == "input") file = e.target.files[0];
else if (action == "drop") file = e.dataTransfer.files[0];
var reader = new FileReader();
reader.onload = (e) => {
this.imgUrl = e.target.result;
};
reader.readAsDataURL(file);
},
imageNull() {
this.imgUrl = "";
},
}
here's validation.js file
extend('img_required', {
validate(imgUrl) {
console.log(imgUrl);
return imgUrl !== "";
},
message() {
return "Image is required!";
}
});
Here I'm checking if imgUrl is an empty base64 string or not.
And when I hit close button, imgUrl nullifies.
The issue here is when I print this imgUrl it shows event.target.files.
I'm new to vue.js. Please tell me if I'm doing something wrong
I think the problem is that you're handleImage function is not correct
Try this;
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
this.imgUrl = reader.result;
};

Unable to upload a file using Vue.js to Lumen backend?

I have tried to upload a file using vue.js as front end technology and laravel in the back end. I have tried to pass the file object using formData javascript object but the server responds as the value is not passed.
I have tried to log the file using console.log and it appropriately displays the data.
Consider that I have discarded some field names.
Template Code
<template>
<b-container>
<div align="center">
<b-card class="mt-4 mb-4 col-md-8" align="left" style="padding: 0 0;">
<card-header slot="header" />
<b-form>
<div class="row">
<div class="col-6 col-md-6">
<b-button
type="submit"
variant="success"
class="float-right col-md-5"
v-if="!update"
#click="save"
squared
>
<i class="fas fa-save"></i>
Save
</b-button>
</div>
</div>
<hr style="margin-top: 10px;" />
<b-form-group
label-cols="12"
label-cols-lg="3"
label-for="input-2"
label="Remark: "
label-align-sm="right"
label-align="left"
>
<b-form-textarea
id="textarea"
v-model="record.remark"
rows="2"
max-rows="3"
></b-form-textarea>
</b-form-group>
<b-form-group
label-cols="12"
label-cols-lg="3"
label-for="input-2"
label="Remark: "
label-align-sm="right"
label-align="left"
>
<b-form-file
v-model="record.attachement"
:state="Boolean(record.attachement)"
placeholder="Choose a file..."
drop-placeholder="Drop file here..."
></b-form-file>
</b-form-group>
</b-form>
<status-message ref="alert" />
</b-card>
</div>
</b-container>
</template>
Script Code
<script>
import { mapGetters, mapActions } from "vuex";
export default {
props: ["id", "user_id"],
data: () => ({
record: {
remark: "",
attachement: null
}
}),
methods: {
...mapActions([
"addBenefitRequest",
]),
save(evt) {
evt.preventDefault();
this.$validator.validate().then(valid => {
if (valid) {
const Attachement = new FormData();
Attachement.append("file", this.record.attachement);
var object = {
remark: this.remark
};
this.addBenefitRequest(object, Attachement);
}
});
},
},
computed: mapGetters([
"getStatusMessage",
"getBenefitRequest",
])
};
</script>
Store Code
async addBenefitRequest({ commit }, object, Attachement) {
try {
const response = await axios.post(
commonAPI.BENEFIT_BASE_URL + "/benefit-requests",
object,
Attachement,
{
headers: {
"Content-Type": "multipart/form-data"
}
}
);
commit("pushBenefitRequest", response.data);
commit("setStatusMessage", "Record has been added.");
} catch (error) {
return error
},
Controller Code
public function store(Request $request, Request $request2)
{
$this->validate($request, [
'employee_id' => 'required|string',
'requested_date' => 'required|date',
// 'benefit_type_id' => 'required|string|exists:benefit_types,id',
'reason' => 'required|string',
]);
$this->validate($request2, [
'attachement' => 'required|image|mimes:jpeg,png,jpg,gif,svg|max:2048'
]);
// $success = BenefitRequest::exists($request->employee_id);
// if(!$success)
// return response()->json("Employee doesn't exist", 422);
$id = (string) Str::uuid();
if($request2->attachement)
{
$attachement = $request2->file('attachement')->store('Benefits');
$request->merge(['attachement' => $attachement]);
}
// $request->attachement = $request->file('attachement')->store('Benefits');
$request->merge(['id' => $id]);
BenefitRequest::create($request->all());
return response()->json('Saved', 201);
}
Route
$router->post('',
['uses' => 'BenefitRequestController#store',
'group'=>'Benefit requests',
'parameter'=>'employee_id, requested_date, requested_by, benefit_type_id, reason, remark, status',
'response'=>'<statusCode, statusMessage>'
]);
Here is an example. you can try it
index.vue
`<div id="app">
<div v-if="!image">
<h2>Select an image</h2>
<input type="file" #change="onFileChange">
</div>
<div v-else>
<img :src="image" />
<button #click="removeImage">Remove image</button>
</div>
</div>`
new Vue({
el: '#app',
data: {
image: ''
},
methods: {
onFileChange(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();
var vm = this;
reader.onload = (e) => {
vm.image = e.target.result;
};
reader.readAsDataURL(file);
},
removeImage: function (e) {
this.image = '';
}
}
})

Multiple file upload using laravel and vue js error not print

Multiple file upload using vue js in laravel my validation is like
$request->validate([
'title' => 'required',
'pics.*' => 'required|image|mimes:jpeg,png,jpg,gif'
]);
and my component is like below
<template>
<div class="col-md-6">
<div class="form-group">
<label>Title</label>
<input id="title" type="text" ref="myDiv" v-model="title" class="form-control" name="title">
<div v-cloak><label class="error" v-if="errors['title']">{{ errors.title[0]
}}</label></div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<label>Upload Files</label>
<input id="uploadfile" type="file" ref="pics" name="pics[]" multiple class="form-control" #change="fieldChange">
<div v-cloak><label class="error" v-if="errors['pics']">{{ errors.pics[0] }}</label></div>
</div>
</div>
</template>
export default {
data(){
return {
attachments:[],
pics : [],
errors: [],
form: new FormData
}
},
methods:{
fieldChange(e){
let selectedFiles=e.target.files;
if(!selectedFiles.length){
return false;
}
for(let i=0;i<selectedFiles.length;i++){
this.attachments.push(selectedFiles[i]);
}
console.log(this.attachments);
},
uploadFile() {
this.errors = [];
this.form.append('img',this.attachments2);
if(this.attachments.length > 0){
for(let i=0; i<this.attachments.length;i++){
this.form.append('pics[]',this.attachments[i]);
}
}else {
this.form.append("pics", '');
}
//this.form.append('')
const config = { headers: { 'Content-Type':
'multipart/form-data' } };
axios.post('/admin/theme/',this.form,config).then(response=>
{
this.pics = [];
}).catch((error) => {
this.errors = error.response.data.errors;
console.log(this.errors.pics);
});
}
},
mounted() {
console.log('Component mounted.')
}
}
If i click to submit button empty title error print but multiple file error not print, i get error but not print
pics.0:["The pics.0 field is required."]
But title field validation error print perfectly
Please anyone help me to print multiple file upload error using vue js

Resources