Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel\Vue - ajax file upload not working on the production server

I have a component where I am uploading a video file, everything works fine on my local machine, and it used to work fine on the production server, Namechap is where I host the project, until only recently I have done some work and made changes, that I saw it doesn't work on the production server anymore.

I am using Vue v. 1.0.28, and this is the upload component, where in the fileInputChange() method I am posting form data, to the /upload endpoint, which on the production server for some reason I can't read in the backend:

<template>
  <div class="card-content col-md-10 col-md-offset-1">

      <div v-if="!uploading">
          <div class="col-md-12 Image-input__input-wrapper">
              Upload video
              <input type="file" name="video" id="video" @change="fileInputChange" class="Image-input__input" accept="video/*">
          </div>
      </div>

      <div class="alert alert-danger video-upload-alert" v-if="failed">Something went wrong. Please check the video format and try again. If you need any help please contact our <a>support service.</a></div>

      <div id="video-form">
          <div class="alert alert-info" v-if="uploading && !failed && !uploadingComplete">
              Please do not navigate away from this page, until the video has finished uploading. Your video will be available at <a href="{{ $root.url }}/videos/{{ uid }}" target="_blank">{{ $root.url }}/videos/{{ uid }}</a>, once uploaded.
          </div>

          <div class="alert alert-success" v-if="uploading && !failed && uploadingComplete">
              Upload complete. Video is now processing. <a href="/videos">Go to your videos</a>.
          </div>

          <div class="progress" v-if="uploading && !failed && !uploadingComplete">
              <div class="progress-bar" v-bind:style="{ width: fileProgress + '%' }"></div>
          </div>

          <div class="row">
              <div class="col-md-12 form-group">
                  <label for="title" class="control-label">Title</label>
                  <input type="text" class="form-control" v-model="title">
              </div>
              <!--
              <div class="col-md-12 form-group">
                  <label for="visibility" class="control-label">Visibility</label>
                  <select class="form-control" v-model="visibility">
                      <option value="private">Private</option>
                      <option value="unlisted">Unlisted</option>
                      <option value="public">Public</option>
                  </select>
              </div>
              -->
          </div>

          <div class="row">
              <div class="col-md-12 form-group">
                  <label for="description" class="control-label">Description</label>
                  <textarea class="form-control" v-model="description"></textarea>
              </div>
          </div>

          <div class="row">
              <div class="col-md-12 form-group">
                  <button type="submit" class="btn btn-submit" @click.prevent="update">Save</button>
              </div>
          </div>
          <div class="row">
              <div class="col-md-12 form-group">
                  <span class="help-block pull-right">{{ saveStatus }}</span>
              </div>
          </div>
  </div>
</template>

<script>
    function initialState (){
        return {
            uid: null,
            uploading: false,
            uploadingComplete: false,
            failed: false,
            title: null,
            link: null,
            description: null,
            visibility: 'private',
            saveStatus: null,
            fileProgress: 0
        }
    }
    export default {
        data: function (){
            return initialState();
        },
        methods: {
            fileInputChange() {
                this.uploading = true;
                this.failed = false;

                this.file = document.getElementById('video').files[0];

                var isVideo = this.isVideo(this.file.name.split('.').pop());

                if (isVideo) {
                  this.store().then(() => {
                      var form = new FormData();

                      form.append('video', this.file);
                      form.append('uid', this.uid);

                      this.$http.post('/upload', form, {
                          progress: (e) => {
                              if (e.lengthComputable) {
                                  this.updateProgress(e)
                              }
                          }
                      }).then(() => {
                          this.uploadingComplete = true
                          this.uploading = false
                      }, () => {
                          this.failed = true
                          this.uploading = false
                      });
                  }, () => {
                      this.failed = true
                      this.uploading = false
                  })
                }
                else {
                  this.failed = true
                  this.uploading = false
                }
            },
            isVideo(extension) {
                switch (extension.toLowerCase()) {
                case 'm4v':
                case 'avi':
                case 'mpg':
                case 'mp4':
                case 'mp3':
                case 'mov':
                case 'wmv':
                case 'flv':
                    return true;
                }
                return false;
            },
            store() {
                return this.$http.post('/videos', {
                    title: this.title,
                    description: this.description,
                    visibility: this.visibility,
                    extension: this.file.name.split('.').pop()
                }).then((response) => {
                    this.uid = response.json().data.uid;
                });
            },
            update() {
                this.saveStatus = 'Saving changes.';

                return this.$http.put('/videos/' + this.uid, {
                    link: this.link,
                    title: this.title,
                    description: this.description,
                    visibility: this.visibility
                }).then((response) => {
                    this.saveStatus = 'Changes saved.';

                    setTimeout(() => {
                        this.saveStatus = null
                    }, 3000)
                }, () => {
                    this.saveStatus = 'Failed to save changes.';
                });
            },
            updateProgress(e) {
                e.percent = (e.loaded / e.total) * 100;
                this.fileProgress = e.percent;
            },
        }
    }
</script>

The problem is that on upload in my controller in the store function the request object is empty on the production server, which I got when I did dd($request->all()). Then it fails to find the video, in the network inspector I get a 404 error, which is returned by firstOrFail() method, because it can't find the Video model, since it is missing $request->uid.

No query results for model [App\Video].

This is the controller:

class VideoUploadController extends Controller
{
    public function index()
    {
      return view('video.upload');
    }

    public function store(Request $request)
    {
      $player = $request->user()->player()->first();

      $video = $player->videos()->where('uid', $request->uid)->firstOrFail();

      $request->file('video')->move(storage_path() . '/uploads', $video->video_filename);

      $this->dispatch(new UploadVideo(
          $video->video_filename
      ));

      return response()->json(null, 200);
    }
}

I am not sure what is going on, since on inspecting the network tab in the console I am sending a request payload that looks like this:

------WebKitFormBoundarywNIkEqplUzfumo0A Content-Disposition: form-data; name="video"; filename="Football Match Play.mp4" Content-Type: video/mp4

------WebKitFormBoundarywNIkEqplUzfumo0A Content-Disposition: form-data; name="uid"

159920a7878cb2 ------WebKitFormBoundarywNIkEqplUzfumo0A--

It is really frustrating since I have no idea how to fix this, when everything is fine on my local machine, not sure what is wrong on the production server and how to fix it?

Update

It started working again on its own, since I have created a ticket in the Namecheap support service, all of sudden after one day, it started working again, I haven't done any changes, and on asking the Namecheap support I have got an answer from them that they haven't done any changes either, so I have no idea what went wrong, which is still a bit frustrating since I would like to avoid that in the future, but at least everything is working now again as it should.

like image 859
Leff Avatar asked Aug 12 '17 13:08

Leff


1 Answers

Laravel's $request->all() method only pulls from the input, thus in the store() method of VideoUploadController $request->all() return empty object.

In your script, it calls this.store() after checked the isVideo is true while file changes. As a result, there isn't a uid parameter or video parameter.

like image 147
Cong Chen Avatar answered Sep 24 '22 03:09

Cong Chen