试图将File对象作为属性传递给组件,并获取一个空对象。我还想知道这是否安全吗?

时间:2018-08-30 00:07:04

标签: javascript laravel security vue.js

我有一个通用的头像预览模式:

<avatar-update :img-file="avatarFile" :show="avatarModalShow" :img-url="url" @close="avatarModalShow = !avatarModalShow" :change-avatar="updateCrop" @destroyUrl="imgUrl = null"> </avatar-update>

提交头像后,我用我的根将一堆属性发送到AvatarUpdate组件。

HTML

<div>
    <label for="avatar" class="cursor-pointer thumbnail-link bg-white p-1 rounded-lg" href="">
        <img class="thumbnail" src="{{asset('images/avatars/avatar-1.png')}}">
    </label>
    <input id="avatar" class="hidden" type="file" @change="onFileChange"/>
</div>

onFileChange: function(e) {
    const file = e.target.files[0];
    this.url = URL.createObjectURL(file);
    this.updateCrop = !this.updateCrop;
    this.avatarModalShow = !this.avatarModalShow;
    this.avatarFile = file;
},

当我在onFileChange函数中console.log文件const时,我得到了文件对象。但是,当我尝试在AvatarUpdate组件中输出{{imgFile}}属性时,我得到了一个空对象。

我想知道这是否安全,是否可以在根目录和AvatarUpdate组件之间操纵文件数据?还有什么使我无法将文件对象作为属性发送和输出的问题?为什么在AvatarUpdate组件上给我一个空对象?

对于这么多问题我感到抱歉,但我之所以将它们包含在一篇文章中,是因为我认为可能存在某些安全功能,阻止我通过组件发送文件对象。

编辑

这是我的AvatarUpload组件:

<modal v-show="show" heading="Avatar Preview" @close="close">
  <div class="flex flex-col">

    <h4 class="text-blue-light mb-5">The avatar will be automatically cropped from the center.</h4>

      <div class="flex flex-col items-center">
          <img class="w-2/3" :src="imgUrl">
      </div>

      <p>{{imgFile}}</p>

      <button class="mt-4 h-10 self-end text-center bg-third-color hover:bg-secondary-color text-white font-bold py-2 px-4 rounded" v-on:click="submitAvatar()">Submit</button>
    </div>

<script>

    export default {
        props: ['show','imgUrl','changeAvatar','imgFile'],
        data() {
          return {
            image: null,
            message: null
          }
        },
        methods: {
            close: function(){
              this.$emit('close');
            },

            submitAvatar: function(){
              console.log(file);
              axios({
                  method: 'POST',
                  url: '/profile/avatar',
                  data: {},
              }).then(function (response) {


              this.message = "Your avatar has been submitted";   

              }.bind(this))
              .catch(function (error) {
                  console.log(error);
              });
            }
        }
    }
</script>

我已经能够从根实例的onFileChange函数中的this.url = URL.createObjectURL(file);获取blob。我正在尝试使用:img-file="avatarFile"属性将整个文件对象发送到AvatarUpdate组件。

通过这种方式,我可以通过Laravel控制器中的请求发送数据:

submitAvatar: function(){
  //Change here!
  var data = new FormData()
    var file = this.imgFile;
    data.append('avatar', file);

  axios({
      method: 'POST',
      url: '/profile/avatar',
      data: data,
  }).then(function (response) {


  this.message = "Your avatar has been submitted";   

  }.bind(this))
  .catch(function (error) {
      console.log(error);
  });
}

Laravel UserController

UserController

public function avatar(Request $request)
{
    return $request->hasFile('avatar');
}

1 个答案:

答案 0 :(得分:1)

在您的代码中,this.avatarFile = file是一个文件(继承自 Blob )对象,不能直接在image src中使用(如果打开浏览器检查器,则img:src的值为[object File],显然,该值不是您所期望的。)

您可以使用Javascript MDN: FileReader.readAsDataURL来达到目标​​。

Javascript MDN: URL.createObjectURL()是另一个解决方案,但是您必须谨慎处理内存管理。选中Javascript MDN: URL.createObjectURL() Usage notes

PS:我建议先将File对象转换为(data-url或object-url),然后再将(data-url或object-url)传递给子组件。直接传递File对象可能会遇到反应性问题。

一个使用 FileReader 的简单演示:

Vue.config.productionTip = false
Vue.component('img-preview', {
  template: `<div>
              <img :src="imageBlob" alt="test"/>
             </div>`,
  props: ['imageBlob']
})
new Vue({
  el: '#app',
  data() {
    return {
      imageObj: null
    }
  },
  methods:{
    onFileChange: function(ev) {
      const selectFile = ev.target.files[0]
      let reader  = new FileReader()
      reader.readAsDataURL(selectFile)
      reader.addEventListener('load', () => {
        this.imageObj = reader.result
        //console.log('select image', reader.result)
      }, false)  
    },
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
  <div>
      <label for="avatar">
          <img-preview :image-blob="imageObj"></img-preview>
      </label>
      <input id="avatar" class="hidden" type="file" @change="onFileChange($event)"/>
  </div>
</div>

一个简单的演示,它使用 createObjectURL

Vue.config.productionTip = false
Vue.component('img-preview', {
  template: `<div>
              <img :src="imageBlob" alt="test"/>
             </div>`,
  props: ['imageBlob']
})
new Vue({
  el: '#app',
  data() {
    return {
      imageObj: null
    }
  },
  methods:{
    onFileChange: function(ev) {
      const selectFile = ev.target.files[0]
      this.imageObj = URL.createObjectURL(selectFile)
    },
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
  <div>
      <label for="avatar">
          <img-preview :image-blob="imageObj"></img-preview>
      </label>
      <input id="avatar" class="hidden" type="file" @change="onFileChange($event)"/>
  </div>
</div>

下面是一个演示(直接将文件对象传递给子组件):

Vue.config.productionTip = false
Vue.component('img-preview', {
  template: `<div>{{imageBlob}}
              <img :src="internalImageObj" alt="test"/>
             </div>`,
  props: ['imageBlob'],
  data() {
    return {
      internalImageObj: ''
    }
  },
  watch: {
    imageBlob: function (newVal) {
      let reader  = new FileReader()
      reader.readAsDataURL(newVal)
      reader.addEventListener('load', () => {
        this.internalImageObj = reader.result
      }, false)  
    }
  }
})
new Vue({
  el: '#app',
  data() {
    return {
      selectedFile: null
    }
  },
  methods:{
    onFileChange: function(ev) {
      this.selectedFile = ev.target.files[0]
    },
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
  <div>
      <label for="avatar">
          <img-preview :image-blob="selectedFile"></img-preview>
      </label>
      <input id="avatar" class="hidden" type="file" @change="onFileChange($event)"/>
  </div>
</div>