简体   繁体   中英

Vue JS - File upload loading progress client side

I'm currently trying to create a small loading bar for when I select a file to upload in my form but I'm really struggling to make sense out of it. Most examples I find online show an upload loading example when we hit a button to 'upload' because it comes with an xmlhttprequest or similar, but in my case, I want to show the loading progress right when I select the file before I hit the button that sends the form. Therefore once I add the file to my input a loading percentage should appear. I'd really appreciate if someone could give an indication as to what needs to be done here.

FileUpload.vue - file upload input component

 <div class="container">
    <!--UPLOAD-->
      <h3 class="form-input-title">{{labelText}}</h3>
      <div class="dropbox">
        <img src="../../assets/img/icon/curr/ic_download.svg"
             class="download-icon"
             alt="download icon"/>
        <input :name="fieldName"  
               :accept="accept"
               @change="loadFile($event)"
               type="file"
               class="input-file">
          <p v-if="!isLoading && !isSuccess">{{text}}</p>
          <p v-if="isLoading">Uploading files... {{uploadLoadPercentage}}</p>
          <p v-if="isSuccess">{{fileName}}</p>
      </div>
  </div>


<script>
  import {ref, watch} from '@vue/composition-api';
  import Validator from "@/utils/yo-validator/YoValidator";

  export default {
    name: 'FileUpload',

    setup(props) {
      /** validator returned error messages **/
      const {errorMessages, validateFieldByData, setFieldData} = Validator.register(props);

      /** watch api error text from parent **/
      const apiError = ref('');
      watch(() => props.apiErrorText, (newVal) => {
        apiError.value = newVal.trim();
      });

      const isLoading = ref(false);
      const isSuccess = ref(false);
      const uploadFailed = ref(false);
      let uploadLoadPercentage = ref(0);
      const fileName = ref('');
      /** watch input in the template **/
      const loadFile = async (event) => {
        // TODO: File loading > When the file is selected, check how long the file (in bytes) takes to upload
        // If the loading is successful proceed without any errors

        // validate the image
        validateFieldByData(event.target.files);

        // define data
        const data = props.isMultiple ? event.target.files : event.target.files[0];

        // set the name of the file that has been uploaded.
        fileName.value = event.target.files[0].name;

        // Once loading and validation is completed, save the data
        saveFile(data);
      };

      const saveFile = (data) => {
        // Once saveFile is triggered, the upload value will be successful.
        isSuccess.value = true;

        // update form data
        setFieldData(data);
        // call the parent if needed
        updateParent(data);
      }
  
      // update parent component
      const updateParent = (data) => {
        if (props.callback) {
          props.callback(data);
        }
      };
      const clearFile = (event) => {
        // clear the value to trigger change event for uploading the same image
        event.target.value = null;
      };

      return {
        errorMessages,
        apiError,
        loadFile,
        clearFile,
        isLoading,
        isSuccess,
        uploadFailed,
        fileName,
        uploadLoadPercentage
      }
    }
  }
</script>

First of all, progress for the file upload has to be tracked when you upload it to some server or to some storage space, normally you make a POST or PUT request, with axios, for example, which has a onUploadProgress callback, with which you can upload your progress tracker, I will not go into detail here, since there are countless examples for this, like: The answer to this SO question , which details this nicely.

If you are in a special case, like not sending this through a simple HTTP request, but through something like firebase, they have builtin tools for this as well, but if you give more detail maybe i would be able to help you more in depth with your problem :D

UPDATE: Okay, sorry, my applogies, I totally misunderstood, what you needed:

 < script > import { ref } from 'vue'; export default { setup() { const reader = new FileReader(); const fileUrl = ref(null); const totalSize = ref(0); const currentProgress = ref('0%'); function handleEvent(event) { if (['loadend', 'load'].includes(event.type)) { console.log('finished loading file'); currentProgress.value = 'Finished loading file'; fileUrl.value = reader.result; } if (event.type === 'progress') { currentProgress.value = `${(event.loaded / totalSize.value).toFixed(2) * 100}%`; console.log('Progress: ', currentProgress.value); console.log('Bytes transferred: ', event.loaded, 'bytes'); } if (event.type === 'loadstart') { totalSize.value = event.total; } } function addListeners(reader) { reader.addEventListener('loadstart', handleEvent); reader.addEventListener('load', handleEvent); reader.addEventListener('loadend', handleEvent); reader.addEventListener('progress', handleEvent); reader.addEventListener('error', handleEvent); reader.addEventListener('abort', handleEvent); //dont do this, make diffrent functions for every //event listener please, your code's readability will be 100% better, //i am on a bus rn, but will make it prettier later :D } function handleSelected(e) { console.log(e); const selectedFile = e.target.files[0]; if (selectedFile) { addListeners(reader); reader.readAsDataURL(selectedFile); } } return { handleSelected, fileUrl, currentProgress }; } }; < /script>
 <template> <div> <div> <label for="img">Choose an image:</label> <input type="file" id="img" name="img" accept="image/*" @change="handleSelected" /> </div> <img v-if="fileUrl" :src="fileUrl" class="preview" height="200" alt="Image preview..." /> <div> <span>Progress: {{ currentProgress }}</span> </div> </div> </template>

Just make a Reader object, which is there to handle these types of situations, and listen for the events emitted by it. Hope this helps, if you have any more questions, please ask :D And this is the output to the console, you can use this data to display on the UI as well:

输出到控制台:

NOTE: In my opinion for images you dont need this progress indicator, since they dont take a long time to upload and will show 100% for most of images(the console output is for a file that is 500mb large), but if you include the http request to upload the file here, then i think it would be ok, if you want to upload regular files, than this is expected and needed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM