Vue 큰 파일 업로드 와 정지점 전송 실현

파일 업로드 2 세트
파일 흐름 기반(form-data)
element-ui 프레임 워 크 의 업로드 구성 요 소 는 기본적으로 파일 흐름 을 기반 으로 합 니 다.
데이터 형식:form-data;
  • 전 달 된 데이터:file 파일 흐름 정보;파일 이름

    클 라 이언 트 가 파일 을 base 64 로 변환 합 니 다.
    fileRead.readAsDataURL(file)을 통 해 base 64 문자열 로 변환 한 후 encodeURIComponent 로 컴 파일 하여 보 내야 합 니 다.보 낸 데 이 터 는 qs.stringify 처 리 를 통 해"Content-Type"을 추가 하 십시오."application/x-ww-form-urlencoded"
    파일 업로드
    우선 element-ui 를 통 해 페이지 를 만 듭 니 다.업로드 의 실현 을 사용자 정의 해 야 하기 때문에 el-uproad 구성 요소 의 auto-uproad 는 false 로 설정 해 야 합 니 다.action 은 필수 매개 변수 입 니 다.값 을 채 우지 않 아 도 됩 니 다.
    
    <template>
      <div id="app">
        <!--      -->
        <el-upload action drag :auto-upload="false" :show-file-list="false" :on-change="handleChange">
          <i class="el-icon-upload"></i>
          <div class="el-upload__text">       , <em>    </em></div>
          <div class="el-upload__tip" slot="tip">      200M    </div>
        </el-upload>
    
        <!--      -->
        <div class="progress-box">
          <span>    :{{ percent.toFixed() }}%</span>
          <el-button type="primary" size="mini" @click="handleClickBtn">{{ upload | btnTextFilter}}</el-button>
        </div>
    
        <!--           -->
        <div v-if="videoUrl">
          <video :src="videoUrl" controls />
        </div>
      </div>
    </template>
    
    
    파일 대상 가 져 오기 및 Array Buffer 대상 으로 전환
    Array Buffer 로 전환 한 것 은 뒤에 SparkMD 5 라 이브 러 리 로 hash 값 을 생 성하 고 파일 이름 을 짓 기 때 문 입 니 다.
    
    async handleChange(file) {
      const fileObj = file.raw
      try{
        const buffer = await this.fileToBuffer(fileObj)
        console.log(buffer)
      }catch(e){
        console.log(e)
      }
    }
    
    
    다음 그림 과 같이 buffer 결 과 를 인쇄 합 니 다.

    메모:before-uproad 함수 와 on-change 함수 의 매개 변 수 는 모두 file 이 있 습 니 다.그러나 on-change 의 file 은 File 대상 이 아 닙 니 다.File 대상 을 얻 으 려 면 file.raw 를 통 해 가 져 와 야 합 니 다.FileReader 클래스 를 사용 하여 File 대상 을 Array Buffer 대상 으로 전환 합 니 다.비동기 프로 세 스 이기 때문에 Promise 로 패키지 합 니 다.
    
    //   File      ArrayBuffer 
    fileToBuffer(file) {
      return new Promise((resolve, reject) => {
        const fr = new FileReader()
        fr.onload = e => {
          resolve(e.target.result)
        }
        fr.readAsArrayBuffer(file)
        fr.onerror = () => {
          reject(new Error('          '))
        }
      })
    }
    
    
    절편 만 들 기
    고정 크기 나 고정 수량 을 통 해 하나의 파일 을 여러 부분 으로 나 눌 수 있 습 니 다.js 가 사용 하 는 IEEE 754 바 이 너 리 부동 소수점 산술 기준 으로 인해 발생 할 수 있 는 오 차 를 피하 기 위해 저 는 고정 크기 의 방식 으로 파일 을 자 르 고 각 절편 의 크기 를 2M,즉 2M=21024 KB=21024*1024 B=2097152 B 로 설정 하기 로 했 습 니 다.파일 을 자 르 는 데 사용 되 는 것 은 Blob.slice()입 니 다.
    
    //         (2M)    ,             
    const chunkSize = 2097152,
      chunkList = [], //          
      chunkListLength = Math.ceil(fileObj.size / chunkSize), //         
      suffix = /\.([0-9A-z]+)$/.exec(fileObj.name)[1] //      
      
    //          hash  
    const spark = new SparkMD5.ArrayBuffer()
    spark.append(buffer)
    const hash = spark.end()
    
    //     ,                 (chunk)          (fileName)
    let curChunk = 0 //         
    for (let i = 0; i < chunkListLength; i++) {
      const item = {
        chunk: fileObj.slice(curChunk, curChunk + chunkSize),
        fileName: `${hash}_${i}.${suffix}` //         hash_1.jpg   
      }
      curChunk += chunkSize
      chunkList.push(item)
    }
    console.log(chunkList)
    
    
    파일 을 선택 하면 다음 그림 과 같은 결 과 를 출력 합 니 다.

    송신 요청
    보 내기 요청 은 병렬 또는 직렬 로 보 낼 수 있 습 니 다.직렬 로 보 내 는 것 을 선택 하 십시오.절편 마다 새로운 요청 이 있 습 니 다.정지점 전송 을 실현 하기 위해 함수 fn 에 봉인 을 요청 합 니 다.요청 집합 을 배열 requestList 로 저장 한 다음 send 함 수 를 봉인 하여 전송 을 요청 합 니 다.그러면 일시 정지 키 를 누 르 면 업로드 가 편리 합 니 다.코드 는 다음 과 같 습 니 다.
    
    sendRequest() {
      const requestList = [] //     
      this.chunkList.forEach(item => {
        const fn = () => {
          const formData = new FormData()
          formData.append('chunk', item.chunk)
          formData.append('filename', item.fileName)
          return axios({
            url: '/single3',
            method: 'post',
            headers: { 'Content-Type': 'multipart/form-data' },
            data: formData
          }).then(res => {
            if (res.data.code === 0) { //   
              if (this.percentCount === 0) {
                this.percentCount = 100 / this.chunkList.length
              }
              this.percent += this.percentCount //     
            }
          })
        }
        requestList.push(fn)
      })
      
      let i = 0 //          
      const send = async () => {
        // if ('  ') return
        if (i >= requestList.length) {
          //     
          return
        } 
        await requestList[i]()
        i++
        send()
      }
      send() //     
    },
    
    
    axios 부분 도 다음 과 같은 형식 으로 직접 쓸 수 있 습 니 다.
    
    axios.post('/single3', formData, {
      headers: { 'Content-Type': 'multipart/form-data' }
    })
    
    
    모든 절편 발송 성공 후
    백 엔 드 인터페이스 에 따라 get 요청 을 하나 더 보 내 고 파일 의 hash 값 을 서버 에 전송 해 야 합 니 다.우 리 는 complete 방법 을 정의 하여 이 를 실현 합 니 다.비디오 파일 로 가정 합 니 다.
    
    const complete = () => {
      axios({
        url: '/merge',
        method: 'get',
        params: { hash: this.hash }
      }).then(res => {
        if (res.data.code === 0) { //       
          this.videoUrl = res.data.path
        }
      })
    }
    
    
    이렇게 하면 파일 전송 에 성공 한 후에 페이지 에서 보 낸 동 영상 을 조회 할 수 있다.
    정지점 전송
    우선 일시 정지 단추 텍스트 처리 입 니 다.필 터 를 사 용 했 습 니 다.upload 값 이 true 이면'일시 정지'를 표시 합 니 다.그렇지 않 으 면'계속'을 표시 합 니 다.
    
    filters: {
      btnTextFilter(val) {
        return val ? '  ' : '  '
      }
    }
    
    
    일시 정지 단 추 를 누 르 면 handle ClickBtn 방법 을 터치 합 니 다.
    
    handleClickBtn() {
      this.upload = !this.upload
      //           
      if (this.upload) this.sendRequest()
    }
    
    
    절편 을 보 내 는 send 방법의 시작 에 if 를 추가 합 니 다(!this.upload)return,이 변 수 를 false 로 업로드 하면 계속 업로드 하지 않 습 니 다.일시 정지 가 끝 난 후에 도 계속 보 낼 수 있 도록 절편 을 성공 적 으로 보 낸 후에 이 절편 을 chunkList 배열 에서 this.chunkList.splice(index,1)를 삭제 해 야 합 니 다.
    코드 집합
    
    <template>
      <div id="app">
        <!--      -->
        <el-upload action drag :auto-upload="false" :show-file-list="false" :on-change="handleChange">
          <i class="el-icon-upload"></i>
          <div class="el-upload__text">       , <em>    </em></div>
          <div class="el-upload__tip" slot="tip">      200M    </div>
        </el-upload>
    
        <!--      -->
        <div class="progress-box">
          <span>    :{{ percent.toFixed() }}%</span>
          <el-button type="primary" size="mini" @click="handleClickBtn">{{ upload | btnTextFilter}}</el-button>
        </div>
    
        <!--           -->
        <div v-if="videoUrl">
          <video :src="videoUrl" controls />
        </div>
      </div>
    </template>
    
    <script>
      import SparkMD5 from "spark-md5"
      import axios from "axios"
      
      export default {
        name: 'App3',
        filters: {
          btnTextFilter(val) {
            return val ? '  ' : '  '
          }
        },
        data() {
          return {
            percent: 0,
            videoUrl: '',
            upload: true,
            percentCount: 0
          }
        },
        methods: {
          async handleChange(file) {
            if (!file) return
            this.percent = 0
            this.videoUrl = ''
            //         ArrayBuffer   
            const fileObj = file.raw
            let buffer
            try {
              buffer = await this.fileToBuffer(fileObj)
            } catch (e) {
              console.log(e)
            }
            
            //         (2M)    ,             
            const chunkSize = 2097152,
              chunkList = [], //          
              chunkListLength = Math.ceil(fileObj.size / chunkSize), //         
              suffix = /\.([0-9A-z]+)$/.exec(fileObj.name)[1] //      
              
            //          hash  
            const spark = new SparkMD5.ArrayBuffer()
            spark.append(buffer)
            const hash = spark.end()
    
            //     ,                 (chunk)          (fileName)
            let curChunk = 0 //         
            for (let i = 0; i < chunkListLength; i++) {
              const item = {
                chunk: fileObj.slice(curChunk, curChunk + chunkSize),
                fileName: `${hash}_${i}.${suffix}` //         hash_1.jpg   
              }
              curChunk += chunkSize
              chunkList.push(item)
            }
            this.chunkList = chunkList // sendRequest    
            this.hash = hash // sendRequest    
            this.sendRequest()
          },
          
          //     
          sendRequest() {
            const requestList = [] //     
            this.chunkList.forEach((item, index) => {
              const fn = () => {
                const formData = new FormData()
                formData.append('chunk', item.chunk)
                formData.append('filename', item.fileName)
                return axios({
                  url: '/single3',
                  method: 'post',
                  headers: { 'Content-Type': 'multipart/form-data' },
                  data: formData
                }).then(res => {
                  if (res.data.code === 0) { //   
                    if (this.percentCount === 0) { //                chunkList        percentCount   
                      this.percentCount = 100 / this.chunkList.length
                    }
                    this.percent += this.percentCount //     
                    this.chunkList.splice(index, 1) //              chunk,      
                  }
                })
              }
              requestList.push(fn)
            })
            
            let i = 0 //          
            //            ,     '/merge'   ,     hash       
            const complete = () => {
              axios({
                url: '/merge',
                method: 'get',
                params: { hash: this.hash }
              }).then(res => {
                if (res.data.code === 0) { //       
                  this.videoUrl = res.data.path
                }
              })
            }
            const send = async () => {
              if (!this.upload) return
              if (i >= requestList.length) {
                //     
                complete()
                return
              } 
              await requestList[i]()
              i++
              send()
            }
            send() //     
          },
          
          //       
          handleClickBtn() {
            this.upload = !this.upload
            //           
            if (this.upload) this.sendRequest()
          },
          
          //   File      ArrayBuffer 
          fileToBuffer(file) {
            return new Promise((resolve, reject) => {
              const fr = new FileReader()
              fr.onload = e => {
                resolve(e.target.result)
              }
              fr.readAsArrayBuffer(file)
              fr.onerror = () => {
                reject(new Error('          '))
              }
            })
          }
        }
      }
    </script>
    
    <style scoped>
      .progress-box {
        box-sizing: border-box;
        width: 360px;
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-top: 10px;
        padding: 8px 10px;
        background-color: #ecf5ff;
        font-size: 14px;
        border-radius: 4px;
      }
    </style>
    
    
    효 과 는 다음 그림 과 같 습 니 다:

    One More Thing
    FormData
    여기 서 데 이 터 를 보 내 는 데 FormData 를 사 용 했 습 니 다.인 코딩 형식 이'multipart/form-data'로 설정 되면 폼 과 같은 형식 을 사용 합 니 다.
    FormData.append()
    FormData 대상 에 존재 하 는 키 에 새 값 을 추가 합 니 다.키 가 존재 하지 않 으 면 이 키 를 추가 합 니 다.이 방법 은 3 개의 인자,formData.append(name,value,filename)를 전달 할 수 있 습 니 다.그 중에서 filename 은 선택 가능 한 매개 변수 로 서버 에 전 달 된 파일 이름 입 니 다.Blob 나 File 이 두 번 째 매개 변수 가 될 때 Blob 대상 의 기본 파일 이름 은'blob'입 니 다.File 대상 의 기본 파일 이름 은 이 파일 의 이름 입 니 다.
    Vue 대 파일 업로드 와 정지점 속보 의 실현 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.더 많은 Vue 대 파일 업로드 와 정지점 속보 내용 은 우리 의 이전 글 을 검색 하거나 아래 의 관련 글 을 계속 조회 하 시기 바 랍 니 다.앞으로 많은 응원 부탁드립니다!
  • 좋은 웹페이지 즐겨찾기