BENderIsGr8te's avatar

File Upload with Vue & Laravel

Hello All, I am building a photo uploader with Vue and Laravel. I seem to have run into a problem I can't figure out. Laravel 5.3 and Vue 1.0+. Here's the current workflow.

  1. User clicks the button that simulates a "click" action on a hidden form input for file
  2. The input file has a v-on:change to call a function to pass the file to my photo component
  3. The Photo component uses Vue Resources to post the file to my app
  4. Laravel gets the post, evaulates and processes and returns a response

This seems pretty simple, the only problem I am getting is that Laravel can't see the file. Vue resource see's it fine because I can successfully get the "Progress" event so I can display a progress bar of the upload. However when I get in Laravel and try to look at files, it shows no files.

Here's the code

Photo.vue

upload: function(){
          // upload the file to the server
          var form = new FormData();
          form.append('photo', this.file, this.file.name);
          
          this.$http.post(this.url, form, {
              progress: function(progress){
                  this.uploadProgress = Math.floor((progress.loaded  / progress.total) * 100);
              }.bind(this)
          }).then(function(r){
              // Completed Successfully
              response = JSON.parse(r.body);

              if( !response.success ) {
                  // The file could not be uploaded, so dispatch an error and remove this element.
                  console.log(response.msg);

                  return;
              }
              // If success, map out the response to the local object
              this.url = response.url;
              this.thumb = response.thumb;

              this.isUploading = false;
          }.bind(this), function(r){
              // Completed with Errors
              console.log(r)
          }.bind(this))
      }

PhotoUploadController.php

public function uploadPhoto($attachToType, $attachToId, Request $request)
    {
         $allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'tiff'];

         if( $attachToType == 'property' ) $attachToType = '\ExpressRentals\Models\ScreeningProperty';
         if( $attachToType == 'listing' ) $attachToType - '\ExpressRentals\Models\Listing';

         // Validate that the file has been uploaded
         if( !$request->hasFile('photo')) {
             return response(['success' => 0, 'msg' => $request->hasFile('File')]);
         }

         // Get the file and check that it's allowed
         $photo = $request->file('photo');

         if(! in_array(strtolower($photo->extension()), $allowedExtensions) ) {
             return response(['success' => 0, 'msg' => 'File Format Not Supported']);
         }

         // Check to make sure the file is larger than 400 pixels wide

         if( false ) {
             return response(['success' => 0, 'msg' => 'Image must be larger than 400px wide']);
         }

         // All is good, save the photo to amazon, create the photo object, then return the photo object for display.

        return response($photo->extension());
    }

Any ideas?

UPDATE

I tried re-writing the function in JQuery, and the same thing is happening. I also was able to see the correct Content-Type of "multipart/form-data" was being received by Laravel. I am using Valet on macOS Sierra, so maybe there is something to do with that?

0 likes
1 reply
lara5153's avatar

Try something like this

  • php artisan make:provider ResponseMacroServiceProvider
namespace App\Providers;

class ResponseMacroServiceProvider extends Illuminate\Support\ServiceProvider
{
    public function boot()
    {
        Illuminate\Support\Facades\Response::macro('success', function ($data) {

            return response()->json([
              'errors' => false,
              'data'   => $data,
            ]);
        });

        Illuminate\Support\Facades\Response::macro('error', function ($message, $status = Illuminate\Http\Response::HTTP_BAD_REQUEST) {

            return response()->json([
              'errors'  => true,
              'message' => $message,
            ], $status);
        });
    }
}
  • add new provider in app/config.php file
  • php artisan make:request PhotoUploadRequest
namespace App\Http\Requests;

class PhotoUploadRequest extends Illuminate\Foundation\Http\FormRequest
{
    public function authorize()
    {
        return true;
    }

    public function rules()
    {
        return [
            'photo' => 'required|mimes:jpg,jpeg,png,gif,tiff'
        ];
    }

    public function response(array $errors)
    {
        return response()->error($errors);
    }
}
  • php artisan make:controller Api/PhotoUploadController
namespace App\Http\Controllers\Api;

class PhotoUploadController extends App\Http\Controllers\Controller
{
    private $attachTypes = [
        'property' => '\App\Models\Foo',
        'listing'  =>'\App\Models\Bar',
        // ...
    ];

    public function upload(App\Http\Requests\PhotoUploadRequest $request, $type)
    {
        try {
            if ($request->hasFile('photo')) {
                $photo = $request->photo;
                $fileName = time() . '.' . $photo->extension();

                if ($path = $photo->storeAs('...', $fileName, 's3') && $photo->isValid()) {
                    $this->attach($type);

                    return response()->success('File sucessfully uploaded: ' . $path);
                }

                throw new Exception('File was\'nt uploaded!');
            }

            throw new Exception('Request has\'nt got any file!');
        } catch (Exception $e) { // in App\Exceptions\Handler's render() method, add your own exception before parent's rendering
            return response()->error($e->getMessage());
        }
    }

    public function attach($type = '')
    {
        if (in_array($type, $this->attachTypes)) {
            $modelName = $this->attachTypes[$type];
            // your db method with exceptions
        }
    }
}
  • In your Vue component file
methods: {
    upload() {
        if (this.file) {
            let formData = new FormData()

            formData.set('photo', this.file) // set the filename with php
            this.isUploading = true

            this.$http.post(this.url, formData, {
                progress: data => {
                    if (data.lengthComputable) {
                        this.uploadProgress = Math.floor(data.loaded  / data.total * 100)
                    }
                }
            }).then(response => {
                if( ! response.errors) {
                    this.url = response.data.url
                    this.thumb = response.data.thumb
                    this.isUploading = false
                }
            }, response => {
                console.error(response)
            })
        }
    }
}

Please or to participate in this conversation.