Yes
el-upload
Whilst the current documentation show the UI side fine, I'm having a really tough time getting the component to work uploading to Laravel 5.4, I keep getting a 422
error saying "you did not send the required json in the body"
-
<el-upload
:action="getEndpoint('/upload/' localType '/' id)" // creates the correct URL to point to, works correctly.
:headers="headerInfo"
:data="fileData"
accept=" .jpg, .jpeg, .png, .bmp, .pdf"
list-type="picture-card"
:on-error="handleUploadError"
:on-preview="handlePictureCardPreview"
:on-remove="handleRemove">
<i class="el-icon-plus"></i>
</el-upload>
I've set the headers to:
headerInfo: {
'Access-Control-Allow-Origin': this.getEndpoint('/upload/' this.type.toLowerCase().trim() '/' this.id),
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS, PUT, DELETE',
'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept, X-File-Name, X-File-Size, X-File-Type'
},
fileData: { //Just to see if the data property actually made a difference. What should go here?
type: this.type,
id: this.id
}
I appreciate some of my question is the upload stuff is new to me, but I've now spent 3 days trying every variation I can think of. Uploading a file should not be this hard! :-D
Below is the response I'm getting from the server when I try to upload.
This is what is getting to the Laravel backend:
I have a Log::info display in the JsonMiddleware handler that prints out the contents of the request.
[2017-08-08 10:53:21] local.INFO: 1. JsonMiddleware->handle $request=POST /api/v1/upload/site/628 HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.8
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, X-File-Name, X-File-Size, X-File-Type
Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE
Access-Control-Allow-Origin: http://localhost:8000/api/v1/upload/site/628
Connection: keep-alive
Content-Length: 50216
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryr5KklRecA5kmCnnw
Host: localhost:8000
Origin: http://localhost:8080
Referer: http://localhost:8080/
User-Agent: Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.109 Mobile Safari/537.36
[2017-08-08 10:53:21] local.INFO: 2. JsonMiddleware->handle $request.getContent()=
The cause of the 422 error is that $request.getContent() is empty- having filled all the header and data values in the el-upload component, I'm not sure what else it wants.
Anyway, a worked example would really, really help. Thanks in advance.
422 error means that you missed required field;
Here is a simple example for Laravel to store an image:
if ($request->hasFile('file')) {
$path = $request->file('file')->store('images');
return response()->json([
'message' => 'Upload success.',
'path' => $path,
'status' => 200
], 200);
}
Thanks @SmileYuhao
I figured it was something like that. Where exactly would I put this? In the data section of the el-input?
Is that in the Controller or the UploadRequest file? I'm still learning laravel... The regular API stuff I'm OK with, the upload is still very new for me.
Would the same thing work for multiple files?
Thanks again for responding.
@adavie1 in Controller in side
public function store(Request $request)
{
if ($request->hasFile('file')) {
$path = $request->file('file')->store('images');
return response()->json([
'message' => 'Upload success.',
'path' => $path,
'status' => 200
], 200);
}
}
Thanks @EmadAdly , I'm testing this now.
Hi all, Still getting the "required json is missing" message. I've got a Log::info as the first line of my controller, it never gets there. JsonMiddleware is interrupting before it gets to the controller.
Routes declaration:
Route::post('/upload/site/{siteId}', 'AttachmentsController@siteUpload')->name('upload.sites');
Controller method:
public function siteUpload (AttachmentRequest $request, $siteId = null)
{
Log::info ('AttachmentsController::siteUpload for site [' . $siteId . '] | request = ' . $request) ;
if ($request->hasFile('file')) {
$path = $request->file('file')->store('images');
return response()->json([
'message' => 'Upload success.',
'path' => $path,
'status' => 200
], 200);
}
}
AttachmentRequest:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class AttachmentRequest extends FormRequest
{
public function rules()
{
return ['filename' => 'required', 'file' => 'mimes:pdf, jpg, jpeg, bmp, png|size:5000'] ;
}
}
The code on the client looks like this:
<template>
<div>
<el-alert type="warning" title="Under Development" description="This still being worked on." show-icon />
<el-upload
:action="getEndpoint('/upload/' + localType + '/' + id)"
:headers="headerInfo"
:data="fileData"
accept=" .jpg, .jpeg, .png, .bmp, .pdf"
list-type="picture-card"
:on-error="handleUploadError"
:on-preview="handlePictureCardPreview"
:on-remove="handleRemove">
<i class="el-icon-plus"></i>
</el-upload>
<el-dialog v-model="dialogVisible" size="tiny">
<img width="100%" :src="dialogImageUrl" alt="">
</el-dialog>
</div>
</template>
<script>
export default {
props: {
id: {
type: [String, Number],
required: true
},
type: {
type: String,
required: true
}
},
data () {
return {
localData: this.rowData,
localType: this.type.toLowerCase().trim(),
dialogImageUrl: '',
dialogVisible: false,
headerInfo: {
'Access-Control-Allow-Origin': this.getEndpoint('/upload/' + this.type.toLowerCase().trim() + '/' + this.id),
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS, PUT, DELETE',
'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept, X-File-Name, X-File-Size, X-File-Type'
},
fileData: {
type: this.type,
id: this.id
}
}
},
methods: {
handleRemove (file, fileList) {
console.log(file, fileList)
},
handlePictureCardPreview (file) {
this.dialogImageUrl = file.url
this.dialogVisible = true
},
handleUploadError (msg, file) {
console.log('Fail Msg:', msg)
console.log('File Details', file)
if (msg.status !== 404) {
this.$notify.error({
title: 'Upload Unsuccessful',
message: 'File [' + file.name + '] unable to uploaded: ' + msg,
duration: 0
})
} else {
this.$notify.error({
title: 'Path Not Found',
message: 'No such path on the server to upload to',
duration: 0
})
}
},
onClick (event) {
}
}
}
</script>
The same component is intended for attaching images to a other parts of the problem domain.
I would really appreciate some pointers on what I'm doing wrong.
I'm making the assumption that the data
prop will send the relevant JSON data, needed satisfy JsonMiddleware's rules. Hence, sending random info in the client code fileData
data item.
I've also tried using the basic Request instead of AttachmentRequest, same issue.
I suppose I could declare a manual upload, but that kind of defeats the purpose of using el-upload at all. Since if I do that, I might as well write the client side as well.
My intended workaround is to use another non-element component for uploads, if I can't get this to work, which I would prefer to avoid.
One last point, I have the backend (Laravel) on one server, and the client code on another, hence all the Cross-origin headers.
Hi @adavie1 , it is great when we are having the same problema and on github there are people looking for the same answers, or similar.
I have been looking for these implementation, after reading some post I have been able to make it work on my case.
// Routes
routes/web.php
Route::post('/uploadPhoto', 'PhotoController@upload');
//Controller
```
public function upload(Request $request)
{
if(!$request->hasFile('file'))
return response()->json([
'error' => 'No File Uploaded'
]);
$file = $request->file('file');
if(!$file->isValid())
return response()->json([
'error' => 'File is not valid!'
]);;
$path= $file->store('public/images');
return response()->json([
'success' => $path
]);
}
// vue component template
action="/uploadPhoto"
:multiple='false'
:headers="{ 'X-CSRF-TOKEN': csrf}"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload">
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #20a0ff;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.avatar {
width: 100%;
height: 178px;
display: block;
}
.el-upload__input{
display: none !important;
}
.el-upload{
display: block !important;
}
In the route I am using the web middleware since I am using the web.php routes and laravel request a CSRF token which I am passing as a prop to the vue component in order to pass it to the request.
in the view I am calling the new created component like this
` <el-upload-photo csrf="{{ csrf_token() }}"></el-upload-photo>
`
Remember to import the element library and create the component, I am doing it at resources/assets/js/.app.js
import Vue from 'vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-default/index.css'
import locale from 'element-ui/lib/locale/lang/en'
Vue.use(ElementUI, { locale });
Vue.component('el-upload-photo', require('./components/el-upload.vue'));
var app = new Vue({
mixins: [require('spark')]
});
```
I hope it helps in some way to you or to another one that is looking for this script.
Thanks @lmatab! That at least gives me new areas to explore. I'll test this tomrrow. Again, thanks for taking the time to put your comment up.
@lmatab How did you solve the 422 error? I'm getting that because there is no form payload data in the el-upload auto-generated request. The message I get is "you did not send the required json in the body"
Laravel is still getting upset because there is no form data passed. Can't get around that issue after many days...
Solved as follows (in JsonApiMiddleware.php):
public function handle($request, Closure $next)
{
if (in_array($request->getMethod(),$this->methods)) {
if (json_decode($request->getContent(),true) === null)
{
if (!$request->hasFile('file')) {
return response(['error' =>'you did not send the required json in the body'], 422);
}
}
else {
$request->merge(json_decode($request->getContent(),true));
}
}
return $next($request);
}
I'm a bit uncomfortable amending a "system" library to get around what I believe is a configuration problem, but I have a client system to deliver, so needs must.
If anyone has a better suggestion, very happy to hear it.
Hi guys!
Not sure if this the right place for my question.
I would like to find out with you guys how can I change the post method from el-upload to something like patch method.
Because what i'm facing so far, is like only i can use the el-upload in individual post, not inside of form with others input elements.
So once i couldn't get it working with a form and other multiple input elements, i was trying to use it passing ID in action like below
<el-upload
class="upload-demo"
drag
ref="upload"
name="file"
:file-list="form.file"
:action="'api/v1/images/news/'+item.id"
method="patch">
<i class="el-icon-upload"></i>
<div class="el-upload__text">Drop file here or <em>click to upload</em></div>
<div class="el-upload__tip" slot="tip">jpg/png files with a size less than 500kb</div>
</el-upload>
But that not working, with Laravel complaining method not allowed,
Is there any way to get this working with a form and other elements, or changing the method at least.
Otherwise I can't see much a use case of this el-upload
Hi guys!
It's look like i've find a way to get it working!
submitting a form with other fields.
So on EU documentation there's option to pass data, where we can get more fields.
So if someone wondering how to get it working, it's just simple like below, what i've done
<el-upload
class="upload-demo"
drag
ref="upload"
name="file"
:data="form"
:action="'/api/v1/news/store?token='+token"
:auto-upload="false"
:on-progress="pogress"
:on-success="onSucess">
<i class="el-icon-upload"></i>
<div class="el-upload__text">Drop file here or <em>click to upload</em></div>
<div class="el-upload__tip" slot="tip">jpg/png files with a size less than 500kb</div>
</el-upload>
Also notice that, if the user won't attach file, it won't submit the form.
So the trick here is to have another alternative to submit the form, one with file and another one without file.
so this the trick i've used by checking the el-upload files length
submitForm () {
this.loading = true
if(this.$refs.upload._data.uploadFiles.length !== 0){
return this.$refs.upload.submit()
}
return axios.post('/api/v1/news/store',this.form).then((response) =>{
this.$emit('news-data',response.data.data)
this.loading = false
this.closeNewsForm()
this.resetForm()
}).catch((error) =>{
this.form.errors = error.response.data.errors
this.loading = false
console.log(error.response.data)
})
},
I hope it can help someone else, and don't be stuck per days like i've been trying to get a work around of this.
Most helpful comment
Hi @adavie1 , it is great when we are having the same problema and on github there are people looking for the same answers, or similar.
I have been looking for these implementation, after reading some post I have been able to make it work on my case.
// Routes
routes/web.php
Route::post('/uploadPhoto', 'PhotoController@upload');
//Controller
```
public function upload(Request $request)
{
if(!$request->hasFile('file'))
return response()->json([
'error' => 'No File Uploaded'
]);
action="/uploadPhoto"
:multiple='false'
:headers="{ 'X-CSRF-TOKEN': csrf}"
:show-file-list="false"
:on-success="handleAvatarSuccess"
:before-upload="beforeAvatarUpload">
import Vue from 'vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-default/index.css'
import locale from 'element-ui/lib/locale/lang/en'
Vue.use(ElementUI, { locale });
Vue.component('el-upload-photo', require('./components/el-upload.vue'));
var app = new Vue({
mixins: [require('spark')]
});
```
I hope it helps in some way to you or to another one that is looking for this script.