Implementierung des Hochladens großer Dateien und des durch Haltepunkte fortsetzbaren Hochladens in Vue

Implementierung des Hochladens großer Dateien und des durch Haltepunkte fortsetzbaren Hochladens in Vue

2 Lösungen für den Dateiupload

Basierend auf Dateistrom (Formulardaten)

Die Upload-Komponente des Element-UI-Frameworks basiert standardmäßig auf Dateistream.

  • Datenformat: Formulardaten;
  • Übertragene Daten: Datei, Dateistream-Informationen, Dateiname, Dateiname

Der Client konvertiert die Datei in Base64

Nach der Konvertierung in einen Base64-String durch fileRead.readAsDataURL(file) muss dieser vor dem Senden mit encodeURIComponent kompiliert werden. Die gesendeten Daten werden von qs.stringify verarbeitet und der Anforderungsheader fügt „Content-Type“ hinzu: „application/x-www-form-urlencoded“

Hochladen großer Dateien

Erstellen Sie zunächst die Seite mit Hilfe von Element-UI. Da Sie eine Upload-Implementierung anpassen möchten, muss der automatische Upload der Komponente „el-upload“ auf „false“ gesetzt werden; „action“ ist ein erforderlicher Parameter und Sie müssen hier keinen Wert eingeben.

<Vorlage>
  <div id="app">
    <!-- Komponente hochladen -->
    <el-upload Aktion ziehen :auto-upload="false" :show-file-list="false" :on-change="handleChange">
      <i Klasse="el-icon-upload"></i>
      <div class="el-upload__text">Ziehen Sie Dateien hierher oder <em>klicken Sie zum Hochladen</em></div>
      <div class="el-upload__tip" slot="tip">Die Videogröße überschreitet nicht 200 MB</div>
    </el-upload>

    <!-- Fortschrittsanzeige-->
    <div Klasse="Fortschrittsbox">
      <span>Upload-Fortschritt: {{ percent.toFixed() }}%</span>
      <el-button type="primary" size="mini" @click="handleClickBtn">{{ hochladen | btnTextFilter}}</el-button>
    </div>

    <!-- Das erfolgreich hochgeladene Video anzeigen-->
    <div v-if="videoUrl">
      <video :src="videoUrl" steuert />
    </div>
  </div>
</Vorlage>

Holen Sie sich das Dateiobjekt und konvertieren Sie es in ein ArrayBuffer-Objekt

Konvertieren Sie in ArrayBuffer, da die SparkMD5-Bibliothek später zum Generieren von Hash-Werten und zum Benennen von Dateien verwendet wird.

async handleChange(Datei) {
  const fileObj = datei.raw
  versuchen{
    const buffer = warte auf dies.fileToBuffer(fileObj)
    console.log(Puffer)
  }fangen(e){
    console.log(e)
  }
}

Das Druckpufferergebnis ist wie folgt


Hinweis: Sowohl die Funktion „Before-Upload“ als auch die Funktion „On-Change“ haben „file“ als Parameter, aber die Datei in „On-Change“ ist kein File-Objekt. Um ein File-Objekt zu erhalten, müssen Sie file.raw verwenden. Die Klasse FileReader wird hier verwendet, um das File-Objekt in ein ArrayBuffer-Objekt umzuwandeln. Da es sich um einen asynchronen Prozess handelt, wird er mit Promise gekapselt:

//Konvertiere das File-Objekt in einen ArrayBuffer 
fileToBuffer(Datei) {
  returniere neues Promise((lösen, ablehnen) => {
    const fr = neuer FileReader()
    fr.onload = e => {
      lösen(e.Ziel.Ergebnis)
    }
    fr.readAsArrayBuffer(Datei)
    fr.onerror = () => {
      reject(new Error('Fehler beim Konvertieren des Dateiformats'))
    }
  })
}

Erstellen von Slices

Eine Datei kann in mehrere Teile mit fester Größe oder fester Zahl aufgeteilt werden. Um den durch den von js verwendeten binären Gleitkomma-Arithmetikstandard IEEE754 verursachten Fehler zu vermeiden, habe ich beschlossen, die Datei in eine feste Größe zu schneiden und die Größe jedes Teils auf 2 M festzulegen, d. h. 2 M = 21024 KB = 21024 * 1024 B = 2097152 B. Blob.slice() wird zum Schneiden von Dateien verwendet

// Datei in feste Größe (2M) aufteilen. Beachten Sie, dass hier mehrere Konstanten deklariert sind const chunkSize = 2097152,
  chunkList = [], // Array zur Aufnahme aller Slices chunkListLength = Math.ceil(fileObj.size / chunkSize), // Gesamtzahl der Slices berechnen suffix = /\.([0-9A-z]+)$/.exec(fileObj.name)[1] // Dateisuffix // Hash-Wert basierend auf dem Dateiinhalt generieren const spark = new SparkMD5.ArrayBuffer()
spark.append(Puffer)
const hash = spark.end()

// Slices generieren. Das Backend erfordert, dass die übergebenen Parameter Byte-Datenblöcke (Chunk) und der Dateiname jedes Datenblocks (FileName) sind.
let curChunk = 0 // Anfangsposition beim Slicen for (let i = 0; i < chunkListLength; i++) {
  konstantes Element = {
    Chunk: DateiObj.slice(aktuellerChunk, aktuellerChunk + Chunkgröße),
    fileName: `${hash}_${i}.${suffix}` // Der Dateiname wird entsprechend hash_1.jpg benannt}
  curChunk += ChunkGröße
  chunkList.push(Element)
}
Konsole.log(ChunkList)

Nach Auswahl einer Datei erhalten Sie ein Druckergebnis wie das folgende:

Anfrage senden

Das Senden von Anfragen kann parallel oder seriell erfolgen. Wählen Sie hier das serielle Senden. Für jeden Slice wird eine neue Anfrage erstellt. Um eine Wiederaufnahme des Haltepunkts zu erreichen, kapseln wir die Anfrage in die Funktion fn, verwenden ein Array requestList, um den Anfragesatz zu speichern, und kapseln dann eine Sendefunktion zum Senden der Anfrage. Auf diese Weise kann der Upload einfach beendet werden, sobald die Pausentaste gedrückt wird. Der Code lautet wie folgt:

sendRequest() {
  const requestList = [] // Sammlung anfordern this.chunkList.forEach(item => {
    const fn = () => {
      const formData = new FormData()
      formData.append('chunk', item.chunk)
      formData.append('Dateiname', item.Dateiname)
      returniere Axios({
        URL: „/single3“,
        Methode: 'post',
        Header: { 'Inhaltstyp': 'multipart/form-data' },
        Daten: formData
      }).dann(res => {
        if (res.data.code === 0) { // Erfolgreichif (this.percentCount === 0) {
            this.percentCount = 100 / this.chunkList.length
          }
          this.percent += this.percentCount // Fortschritt ändern}
      })
    }
    requestList.push(fn)
  })
  
  let i = 0 // Anzahl der gesendeten Anfragen aufzeichnen const send = async () => {
    // wenn ('Pause') zurück
    if (i >= Anfrageliste.Länge) {
      //Ausgefüllte Retoure senden
    } 
    warte auf requestList[i]()
    ich++
    schicken()
  }
  send() // Anfrage senden},

Der Axios-Teil kann auch direkt in der folgenden Form geschrieben werden:

axios.post('/single3', formData, {
  Header: { 'Inhaltstyp': 'multipart/form-data' }
})

Nachdem alle Slices erfolgreich gesendet wurden

Entsprechend der Backend-Schnittstelle wird eine weitere Get-Anfrage gesendet und der Hashwert der Datei an den Server übergeben. Wir definieren eine vollständige Methode zur Implementierung. Dabei wird davon ausgegangen, dass es sich bei der gesendeten Datei um eine Videodatei handelt.

const vollständig = () => {
  axios({
    URL: „/zusammenführen“,
    Methode: 'get',
    Parameter: { Hash: this.hash }
  }).dann(res => {
    if (res.data.code === 0) { // Anfrage erfolgreich gesendet this.videoUrl = res.data.path
    }
  })
}

Auf diese Weise können Sie das gesendete Video auf der Seite durchsuchen, nachdem die Datei erfolgreich gesendet wurde.

Lebenslauf herunterladen

Zuerst wird der Text der Pausenschaltfläche verarbeitet. Dabei wird ein Filter angewendet. Wenn der Upload-Wert wahr ist, wird „Pause“ angezeigt, andernfalls „Weiter“:

Filter:
  btnTextFilter(Wert) {
    Rückgabewert? 'Pause': 'Weiter'
  }
}

Wenn die Pause-Taste gedrückt wird, wird die Methode handleClickBtn ausgelöst

handleClickBtn() {
  dies.upload = !dies.upload
  // Wenn nicht pausiert, Hochladen fortsetzen if (this.upload) this.sendRequest()
}

Fügen Sie am Anfang der Sendemethode „if (!this.upload) return“ hinzu, um den Slice zu senden, sodass der Upload nicht fortgesetzt wird, solange die Upload-Variable „false“ ist. Um nach der Pause mit dem Senden fortzufahren, müssen Sie nach jedem erfolgreichen Senden eines Slices den Slice aus dem ChunkList-Array löschen: this.chunkList.splice(index, 1)

Code-Zusammenfassung

<Vorlage>
  <div id="app">
    <!-- Komponente hochladen -->
    <el-upload Aktion ziehen :auto-upload="false" :show-file-list="false" :on-change="handleChange">
      <i Klasse="el-icon-upload"></i>
      <div class="el-upload__text">Ziehen Sie Dateien hierher oder <em>klicken Sie zum Hochladen</em></div>
      <div class="el-upload__tip" slot="tip">Die Videogröße überschreitet nicht 200 MB</div>
    </el-upload>

    <!-- Fortschrittsanzeige-->
    <div Klasse="Fortschrittsbox">
      <span>Upload-Fortschritt: {{ percent.toFixed() }}%</span>
      <el-button type="primary" size="mini" @click="handleClickBtn">{{ hochladen | btnTextFilter}}</el-button>
    </div>

    <!-- Das erfolgreich hochgeladene Video anzeigen-->
    <div v-if="videoUrl">
      <video :src="videoUrl" steuert />
    </div>
  </div>
</Vorlage>

<Skript>
  SparkMD5 von „spark-md5“ importieren
  Axios von „Axios“ importieren
  
  Standard exportieren {
    Name: 'App3',
    Filter:
      btnTextFilter(Wert) {
        Rückgabewert? 'Pause': 'Weiter'
      }
    },
    Daten() {
      zurückkehren {
        Prozent: 0,
        Video-URL: '',
        hochladen: wahr,
        ProzentAnzahl: 0
      }
    },
    Methoden: {
      async handleChange(Datei) {
        wenn (!Datei) zurückgeben
        dies.Prozent = 0
        diese.videoUrl = ''
        // Datei abrufen und in ein ArrayBuffer-Objekt konvertieren const fileObj = file.raw
        Puffer lassen
        versuchen {
          Puffer = warte auf dies.fileToBuffer(fileObj)
        } fangen (e) {
          console.log(e)
        }
        
        // Datei in feste Größe (2M) aufteilen. Beachten Sie, dass hier mehrere Konstanten deklariert sind const chunkSize = 2097152,
          chunkList = [], // Array zur Aufnahme aller Slices chunkListLength = Math.ceil(fileObj.size / chunkSize), // Gesamtzahl der Slices berechnen suffix = /\.([0-9A-z]+)$/.exec(fileObj.name)[1] // Dateisuffix // Hash-Wert basierend auf dem Dateiinhalt generieren const spark = new SparkMD5.ArrayBuffer()
        spark.append(Puffer)
        const hash = spark.end()

        // Slices generieren. Das Backend erfordert, dass die übergebenen Parameter Byte-Datenblöcke (Chunk) und der Dateiname jedes Datenblocks (FileName) sind.
        let curChunk = 0 // Anfangsposition beim Slicen for (let i = 0; i < chunkListLength; i++) {
          konstantes Element = {
            Chunk: DateiObj.slice(aktuellerChunk, aktuellerChunk + Chunkgröße),
            fileName: `${hash}_${i}.${suffix}` // Der Dateiname wird entsprechend hash_1.jpg benannt}
          curChunk += ChunkGröße
          chunkList.push(Element)
        }
        this.chunkList = chunkList // sendRequest muss this.hash = hash verwenden // sendRequest muss this.sendRequest() verwenden
      },
      
      // Anfrage senden sendRequest() {
        const requestList = [] // Sammlung anfordern this.chunkList.forEach((item, index) => {
          const fn = () => {
            const formData = new FormData()
            formData.append('chunk', item.chunk)
            formData.append('Dateiname', item.Dateiname)
            returniere Axios({
              URL: „/single3“,
              Methode: 'post',
              Header: { 'Inhaltstyp': 'multipart/form-data' },
              Daten: formData
            }).dann(res => {
              if (res.data.code === 0) { // Erfolgreichif (this.percentCount === 0) { // Vermeiden Sie das Löschen des Slices nach erfolgreichem Upload und das Ändern der Länge von chunkList, um den Wert von percentCount zu beeinflussenthis.percentCount = 100 / this.chunkList.length
                }
                this.percent += this.percentCount // Fortschritt ändern this.chunkList.splice(index, 1) // Sobald der Upload erfolgreich war, diesen Block löschen, um die Wiederaufnahme des Haltepunkts zu ermöglichen}
            })
          }
          requestList.push(fn)
        })
        
        let i = 0 // Zeichne die Anzahl der gesendeten Anfragen auf // Nachdem alle Dateisegmente gesendet wurden, musst du die Schnittstelle „/merge“ anfordern und den Datei-Hash an den Server übergeben const complete = () => {
          axios({
            URL: „/zusammenführen“,
            Methode: 'get',
            Parameter: { Hash: this.hash }
          }).dann(res => {
            if (res.data.code === 0) { // Anfrage erfolgreich gesendet this.videoUrl = res.data.path
            }
          })
        }
        const send = async () => {
          wenn (!this.upload) return
          if (i >= Anfrageliste.Länge) {
            // Senden abgeschlossen()
            zurückkehren
          } 
          warte auf requestList[i]()
          ich++
          schicken()
        }
        send() // Anfrage senden},
      
      // Drücken Sie die Pause-Taste handleClickBtn() {
        dies.upload = !dieses.upload
        // Wenn nicht pausiert, Hochladen fortsetzen if (this.upload) this.sendRequest()
      },
      
      //Konvertiere das File-Objekt in einen ArrayBuffer 
      fileToBuffer(Datei) {
        returniere neues Promise((lösen, ablehnen) => {
          const fr = neuer FileReader()
          fr.onload = e => {
            lösen(e.Ziel.Ergebnis)
          }
          fr.readAsArrayBuffer(Datei)
          fr.onerror = () => {
            reject(new Error('Fehler beim Konvertieren des Dateiformats'))
          }
        })
      }
    }
  }
</Skript>

<Stilbereich>
  .Fortschrittsbox {
    Box-Größe: Rahmenbox;
    Breite: 360px;
    Anzeige: Flex;
    Inhalt ausrichten: Abstand dazwischen;
    Elemente ausrichten: zentrieren;
    Rand oben: 10px;
    Polsterung: 8px 10px;
    Hintergrundfarbe: #ecf5ff;
    Schriftgröße: 14px;
    Rahmenradius: 4px;
  }
</Stil>

Die Wirkung ist wie folgt:

Noch etwas

Formulardaten

FormData wird hier zum Senden von Daten verwendet. Wenn der Kodierungstyp auf „multipart/form-data“ eingestellt ist, wird dasselbe Format wie das Formular verwendet.

FormData.append()

Einem vorhandenen Schlüssel im FormData-Objekt wird ein neuer Wert hinzugefügt, oder der Schlüssel wird hinzugefügt, wenn er nicht vorhanden ist. Diese Methode kann drei Parameter übergeben: formData.append(Name, Wert, Dateiname), wobei Dateiname ein optionaler Parameter ist und der an den Server übergebene Dateiname ist. Wenn ein Blob oder eine Datei als zweiter Parameter verwendet wird, lautet der Standarddateiname des Blob-Objekts „blob“. Der Standarddateiname für ein Dateiobjekt ist der Name der Datei.

Dies ist das Ende dieses Artikels über die Implementierung des Hochladens großer Dateien und der Wiederaufnahme von Haltepunkten in Vue. Weitere verwandte Inhalte zum Hochladen großer Dateien und der Wiederaufnahme von Haltepunkten in Vue finden Sie in den vorherigen Artikeln von 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, dass jeder 123WORDPRESS.COM in Zukunft unterstützen wird!

Das könnte Sie auch interessieren:
  • Vue + Element + OSS realisiert das Hochladen von Front-End-Fragmenten und die Wiederaufnahme von Haltepunkten
  • Basierend auf Vue-Simple-Uploader, kapselt die globale Upload-Plug-In-Funktion des Dateisegment-Uploads, des sofortigen Uploads und der Breakpoint-Fortsetzung

<<:  So lösen Sie das Zeichensatzproblem bei der Anmeldung bei Linux

>>:  Tutorial zur Installation der dekomprimierten Version von mysql-8.0.15-winx64 und drei Möglichkeiten zum Beenden

Artikel empfehlen

So konfigurieren Sie die Linux-Firewall und öffnen die Ports 80 und 3306

Port 80 ist ebenfalls konfiguriert. Geben Sie zun...

Beispiel für die Installation von nginx in einem angegebenen Verzeichnis

Aufgrund von Unternehmensanforderungen müssen zwe...

SQL zur Implementierung der Wiederherstellung einer Zeitreihenversetzung

Inhaltsverzeichnis 1. Anforderungsbeschreibung 2....

Lernhinweise zum WeChat-Applet: Seitenkonfiguration und -routing

Ich habe kürzlich die Entwicklung kleiner Program...

Bringen Sie Ihnen kostenlos bei, wie Sie AWS-Serverressourcen nutzen

AWS – Amazons Cloud-Computing-Serviceplattform Ic...

15 Linux-Befehlsaliase, die Ihnen Zeit sparen

Vorwort Bei der Verwaltung und Wartung des Linux-...

Detaillierter Informationsaustausch über das MySQL-Protokollsystem

Jeder, der schon einmal an einem großen System ge...