React+Koa-Beispiel zur Implementierung des Datei-Uploads

React+Koa-Beispiel zur Implementierung des Datei-Uploads

Hintergrund

Als ich vor Kurzem mein Abschlussprojekt schrieb, war ich an einigen Datei-Upload-Funktionen beteiligt, darunter normaler Datei-Upload, Upload großer Dateien, Fortsetzen von Haltepunkten usw.

Serverabhängigkeiten

  • koa (Node.js-Framework)
  • koa-router (Koa-Routing)
  • koa-body (Middleware zur Koa-Body-Analyse, die zum Analysieren von Inhalten nach Anfragen verwendet werden kann)
  • koa-static-cache (Koa-Middleware für statische Ressourcen, wird zur Verarbeitung von Anfragen zu statischen Ressourcen verwendet)
  • koa-bodyparser (den Inhalt von request.body analysieren)

Domänenübergreifende Backend-Konfiguration

app.use(async (ctx, next) => {
 ctx.set('Zugriffskontrolle-Origin-erlauben', '*');
 ctx.set(
  'Zugriffskontrolle-Header zulassen',
  'Inhaltstyp, Inhaltslänge, Autorisierung, Akzeptieren, X-Angefordert-Mit, IhrHeaderFeld',
 );
 ctx.set('Zugriffskontrolle-Zulassen-Methoden', 'PUT, POST, GET, DELETE, OPTIONEN');
 if (ctx.method == 'OPTIONEN') {
  ctx.Körper = 200;
 } anders {
  warte auf das nächste();
 }
});

Der statische Ressourcenzugriff der Backend-Konfiguration verwendet Koa-Static-Cache

// Statische Ressourcenverarbeitung app.use(
 KoaStaticCache('./pulbic', {
  Präfix: '/public',
  dynamisch: wahr,
  gzip: wahr,
 }),
);

Die Analyse des Anforderungstexts der Backend-Konfiguration verwendet koa-bodyparser.

const bodyParser = erforderlich('koa-bodyparser');
app.verwenden(bodyParser());

Front-End-Abhängigkeiten

  • Reagieren
  • Antd
  • Achsen

Normaler Dateiupload

hinteres Ende

Das Backend muss nur koa-body verwenden, um Optionen als Middleware zu konfigurieren und es an router.post('url',middleware,callback) zu übergeben.

Backend-Code

 // Konfiguration hochladen const uploadOptions = {
// Unterstützt das Dateiformat multipart: true,
 gewaltig:
  // Laden Sie das Verzeichnis direkt in den öffentlichen Ordner hoch. Denken Sie daran, / nach dem Ordner hinzuzufügen, um den Zugriff zu erleichtern
  UploadDir: Pfad.Join(__Dirname, '../../pulbic/'),
  // Dateierweiterungen beibehalten keepExtensions: true,
 },
};
router.post('/upload', neuer KoaBody(uploadOptions), (ctx, weiter) => {
 // Hochgeladene Datei abrufen const file = ctx.request.files.file;
 const fileName = file.path.split('/')[file.path.split('/').Länge-1];
 ctx.body = {
   Code: 0,
   Daten:{
    URL: `öffentlich/${fileName}`
   },
   Nachricht: „Erfolgreich“

 }
});

Frontend

Hier verwende ich die formData-Übertragungsmethode. Das Frontend greift über <input type='file'/> auf den Dateiselektor zu und erhält die ausgewählte Datei über das onChange-Ereignis e.target.files[0]. Erstellen Sie dann ein FormData-Objekt, um die erhaltene Datei formData.append('file',targetFile) abzurufen.

Front-End-Code

   const Hochladen = () => {
   const [url, setUrl] = useState<string>('')
   const handleClickUpload = () => {
     const fileLoader = document.querySelector('#btnFile') als HTMLInputElement;
     wenn (istNil(fileLoader)) {
       zurückkehren;
     }
     fileLoader.click();
   }
   const handleUpload = async (e: beliebig) => {
     //Hochgeladene Datei abrufen const file = e.target.files[0];
     const formData = new FormData()
     formData.append('Datei', Datei);
     // Datei hochladen const { data } = await uploadSmallFile(formData);
     konsole.log(daten.url);
     setUrl(`${baseURL}${data.url}`);
   }
   zurückkehren (
     <div>
       <input type="file" id="btnFile" onChange={handleUpload} style={{ display: 'none' }} />
       <Button onClick={handleClickUpload}>Kleine Datei hochladen</Button>
       <img src={url} />
     </div>
   )
 }

Andere Optionen

  • input+form Legt die Aktion des Formulars auf der Backend-Seite fest, enctype="multipart/form-data", type='post'
  • Die Verwendung von fileReader zum Lesen von Dateidaten zum Hochladen ist nicht sehr kompatibel

Hochladen großer Dateien

Beim Hochladen einer Datei kann es zu einer Zeitüberschreitung der Anforderung kommen, weil die Datei zu groß ist. In diesem Fall können Sie die Fragmentierungsmethode verwenden. Einfach ausgedrückt wird die Datei in kleine Teile aufgeteilt und an den Server gesendet. Diese kleinen Teile identifizieren, zu welcher Datei sie gehören und an welcher Position sie sich befinden. Nachdem alle kleinen Teile übertragen wurden, führt das Backend eine Zusammenführung aus, um diese Dateien zu einer vollständigen Datei zusammenzuführen und den gesamten Übertragungsprozess abzuschließen.

Frontend

  • Das Abrufen der Datei erfolgt auf die gleiche Weise wie zuvor, daher werde ich nicht ins Detail gehen.
  • Legen Sie die Standardfragmentgröße und den Datei-Slice fest. Jeder Slice-Name lautet Dateiname.index.ext. Führen Sie die Anforderung rekursiv aus, bis die gesamte Datei gesendet und die Anforderung zusammengeführt wurde.
  const handleUploadLarge = async (e: beliebig) => {
     //Hochgeladene Datei abrufen const file = e.target.files[0];
     // Für Dateisegmente warten Sie auf uploadEveryChunk(file, 0);
   }
   const uploadEveryChunk = (
     Datei: Datei,
     Index: Nummer,
   ) => {
     konsole.log(index);
     const chunkSize = 512; // Slice-Breite // [Dateiname, Dateiendung]
     const [fname, fext] = datei.name.split('.');
     // Startbyte des aktuellen Slices abrufen const start = index * chunkSize;
     if (start > datei.größe) {
       // Wenn die Dateigröße überschritten wird, stoppen Sie das rekursive Hochladen return mergeLargeFile(file.name);
     }
     const blob = Datei.Slice(Start, Start + Chunkgröße);
     // Benennen Sie jedes Teil const blobName = `${fname}.${index}.${fext}`;
     const blobFile = neue Datei([blob], blobName);
     const formData = new FormData();
     formData.append('Datei', Blob-Datei);
     ladeGroßeDatei hoch(formData).then((res) => {
       // Rekursiver Upload uploadEveryChunk(Datei, ++Index);
     });
   };

hinteres Ende

Das Backend muss zwei Schnittstellen bereitstellen

Hochladen

Speichern Sie jeden hochgeladenen Block in einem Ordner mit dem entsprechenden Namen, um ihn später einfach zusammenführen zu können.

const uploadStencilPreviewOptions = {
mehrteilig: wahr,
gewaltig:
 uploadDir: path.resolve(__dirname, '../../temp/'), // Dateispeicheradresse keepExtensions: true,
 maxFeldgröße: 2 * 1024 * 1024,
},
};

router.post('/upload_chunk', neuer KoaBody(uploadStencilPreviewOptions), async (ctx) => {
versuchen {
 const Datei = ctx.request.files.Datei;
 // [Name, Index, Erw] – Dateinamen aufteilen const fileNameArr = file.name.split('.');

 const UPLOAD_DIR = Pfad.resolve(__dirname, '../../temp');
 // Verzeichnis zum Speichern von Slices const chunkDir = `${UPLOAD_DIR}/${fileNameArr[0]}`;
 wenn (!fse.existsSync(chunkDir)) {
  // Verzeichnis erstellen, wenn kein Verzeichnis vorhanden ist // Temporäres Verzeichnis für große Dateien erstellen await fse.mkdirs(chunkDir);
 }
 // Ursprüngliche Datei name.index – die spezifische Adresse und der Name jedes Shards const dPath = path.join(chunkDir, fileNameArr[1]);

 // Verschiebe die fragmentierten Dateien vom Temp- in das temporäre Verzeichnis der diesmal hochgeladenen großen Datei. await fse.move(file.path, dPath, { overwrite: true });
 ctx.body = {
  Code: 0,
  Meldung: „Datei erfolgreich hochgeladen“,
 };
} fangen (e) {
 ctx.body = {
  Code: -1,
  Meldung: `Dateiupload fehlgeschlagen: ${e.toString()}`,
 };
}
});

verschmelzen

Gemäß der Zusammenführungsanforderung vom Frontend wird der mitgeführte Name verwendet, um den zum Namen gehörenden Ordner im temporären Cache großer Dateiblöcke zu finden. Nach dem Lesen der Blöcke in Indexreihenfolge werden die Dateien mithilfe von fse.appendFileSync (Pfad, Daten) zusammengeführt (in der Reihenfolge anhängen bedeutet Zusammenführen) und anschließend wird der temporäre Speicherordner gelöscht, um Speicherplatz freizugeben.

router.post('/merge_chunk', async (ctx) => {
 versuchen {
  const { Dateiname } = ctx.request.body;
  const fname = Dateiname.split('.')[0];
  const TEMP_DIR = Pfad.resolve(__dirname, '../../temp');
  const static_preview_url = "/öffentliche/Vorschauen";
  const STORAGE_DIR = Pfad.resolve(__dirname, `../..${static_preview_url}`);
  const chunkDir = Pfad.join(TEMP_DIR, fname);
  const chunks = warte auf fse.readdir(chunkDir);
  Brocken
   .sort((a, b) => a - b)
   .map((chunkPath) => {
    // Dateien zusammenführen fse.appendFileSync(
     Pfad.join(STORAGE_DIR, Dateiname),
     fse.readFileSync(`${chunkDir}/${chunkPath}`),
    );
   });
  // Löschen Sie den temporären Ordner fse.removeSync(chunkDir);
  // Die URL für den Zugriff auf das Bild
  const url = `http://${ctx.request.header.host}${static_preview_url}/${fileName}`;
  ctx.body = {
   Code: 0,
   Daten: { URL },
   Nachricht: ‚Erfolg‘,
  };
 } fangen (e) {
  ctx.body = { code: -1, message: `Zusammenführung fehlgeschlagen: ${e.toString()}` };
 }
});

Lebenslauf herunterladen

Wenn bei der Übertragung großer Dateien die Übertragung aufgrund einer Seitenaktualisierung oder eines vorübergehenden Fehlers fehlschlägt, muss die Datei von Anfang an erneut übertragen werden, was für den Benutzer sehr unangenehm ist. Daher ist es notwendig, den Ort zu markieren, an dem die Übertragung fehlgeschlagen ist, und das nächste Mal direkt hierher zu übertragen. Ich verwende die Methode zum Lesen und Schreiben im localStorage.

  const handleUploadLarge = async (e: beliebig) => {
    //Hochgeladene Datei abrufen const file = e.target.files[0];
    const Datensatz = JSON.parse(localStorage.getItem('uploadRecord') als beliebig);
    wenn (!isNil(Datensatz)) {
      // Der Einfachheit halber werden wir das Kollisionsproblem hier nicht berücksichtigen. Um festzustellen, ob die Dateien gleich sind, können wir die Hash-Dateimethode verwenden. // Bei großen Dateien können wir die Hash-Methode (eine Datei + Dateigröße) verwenden, um festzustellen, ob zwei Dateien gleich sind if (record.name === file.name) {
        Rückgabe: warte auf UploadEveryChunk (Datei, Datensatz.Index);
      }
    }
    // Für Dateisegmente warten Sie auf uploadEveryChunk(file, 0);
  }
  const uploadEveryChunk = (
    Datei: Datei,
    Index: Nummer,
  ) => {
    const chunkSize = 512; // Slice-Breite // [Dateiname, Dateiendung]
    const [fname, fext] = datei.name.split('.');
    // Startbyte des aktuellen Slices abrufen const start = index * chunkSize;
    if (start > datei.größe) {
      // Wenn die Dateigröße überschritten wird, rekursives Hochladen beenden return mergeLargeFile(file.name).then(()=>{
        // Löschen Sie den Datensatz, nachdem die Zusammenführung erfolgreich war. localStorage.removeItem('uploadRecord')
      });
    }
    const blob = Datei.Slice(Start, Start + Chunkgröße);
    // Benennen Sie jedes Teil const blobName = `${fname}.${index}.${fext}`;
    const blobFile = neue Datei([blob], blobName);
    const formData = new FormData();
    formData.append('Datei', Blob-Datei);
    ladeGroßeDatei hoch(formData).then((res) => {
      // Nachdem jedes Datenelement erfolgreich übertragen wurde, wird der Datensatzspeicherort aufgezeichnet localStorage.setItem('uploadRecord',JSON.stringify({
        Name:Dateiname,
        Index:Index+1
      }))
      // Rekursiver Upload uploadEveryChunk(Datei, ++Index);
    });
  };

Dateiidentifikation

Sie können MD5, Hash usw. der Datei berechnen. Wenn die Datei zu groß ist, kann das Hashing lange dauern. Sie können einen Teil der Datei nehmen und ihn mit der Dateigröße hashen, um einen lokalen Stichprobenvergleich durchzuführen. Hier ist der Code zum Berechnen von MD5 mithilfe der Crypto-JS-Bibliothek und zum Lesen der Datei mit FileReader

// MD5 berechnen, um zu sehen, ob es bereits existiert const sign = tempFile.slice(0, 512);
   const signFile = neue Datei(
    [Zeichen, (tempFile.size als unbekannt) als BlobPart],
    '',
   );
   Konstante Leser = neuer FileReader();
   reader.onload = Funktion (Ereignis) {
    const binary = Ereignis?.Ziel?.Ergebnis;
    const md5 = binär und CryptoJs.MD5 (binär als Zeichenfolge).toString ();
    const Datensatz = localStorage.getItem('upLoadMD5');
    wenn (isNil(md5)) {
     const Datei = blobToFile(blob, `${getRandomFileName()}.png`);
     returniere UploadPreview(Datei, 0, md5);
    }
    const Datei = blobToFile(blob, `${md5}.png`);
    wenn (isNil(Datensatz)) {
     // Dieses MD5 direkt hochladen und aufzeichnen
     returniere UploadPreview(Datei, 0, md5);
    }
    const recordObj = JSON.parse(Datensatz);
    wenn (DatensatzObj.md5 == md5) {
     // Hochladen von der aufgezeichneten Position aus starten // Hochladen vom Haltepunkt aus fortsetzen return uploadPreview(file, recordObj.index, md5);
    }
    returniere UploadPreview(Datei, 0, md5);
   };
   : Der Leser liest den Binärcode aus dem Leseordner.

Zusammenfassen

Ich wusste vorher nicht viel über das Hochladen von Dateien. Durch diese Funktion meines Abschlussprojekts habe ich ein vorläufiges Verständnis der Front-End- und Back-End-Codes zum Hochladen von Dateien. Vielleicht sind diese Methoden nur einige der Optionen und decken nicht alle ab. Ich hoffe, dass ich mich in zukünftigen Studien weiter verbessern kann.
Dies ist mein erster Blog auf Nuggets. Nach der Teilnahme am Praktikum habe ich festgestellt, dass mein Wissen nicht ausreicht. Ich hoffe, mein Wissenssystem zu ordnen und meinen Lernprozess aufzuzeichnen, indem ich weiter blogge. Ich hoffe auch, dass die Meister mir Ratschläge geben, wenn ich auf Probleme stoße. Danke

Oben sind die Details des Beispiels für die Implementierung des Datei-Uploads durch React+Koa aufgeführt. Weitere Informationen zur Implementierung des Datei-Uploads durch React+Koa finden Sie in den anderen verwandten Artikeln auf 123WORDPRESS.COM!

Das könnte Sie auch interessieren:
  • React-Implementierungsbeispiel zum Hochladen von Dateien auf Alibaba Cloud OSS
  • Der React Quill-Bildupload wird von der Standardkonvertierung auf Base64 geändert, um auf den Server hochzuladen
  • React Native verwendet Fetch zum Hochladen von Bildern
  • Beispielcode zum Hochladen von Bildern zu Qiniu in React
  • React+react-dropzone+node.js-Beispielcode zum Hochladen von Bildern
  • React Native implementiert ein Beispiel zum Hochladen von Netzwerkbildern auf den Server
  • ReactNative-Beispielcode zur Implementierung der Bild-Upload-Funktion
  • React + Ajax + Java, um die Funktion zum Hochladen und Voranzeigen von Bildern zu realisieren
  • Beispielcode für die Implementierung der knotenbasierten React-Bild-Upload-Komponente
  • React-Beispiel, das den Fortschritt des Datei-Uploads zeigt

<<:  Tutorial zum Migrieren von Projekten von MYSQL zu MARIADB

>>:  Detaillierte Erklärung der MySQL-Gruppensortierung, um die Top N zu finden

Artikel empfehlen

Beispielcode zur Implementierung der PC-Auflösungsanpassung in Vue

Inhaltsverzeichnis planen Abhängigkeiten installi...

So migrieren Sie SQLite zu einem MySQL-Skript

Ohne weitere Umschweife werde ich den Code direkt...

So lösen Sie das Problem, dass Tomcat9.exe abstürzt

Ein Leser kontaktierte mich und fragte, warum es ...

So entwerfen und optimieren Sie MySQL-Indizes

Inhaltsverzeichnis Was ist ein Index? Prinzip der...

Web-Frontend-Entwicklung CSS-bezogene Teamzusammenarbeit

Die Frontend-Entwicklungsabteilung wächst, die Mi...

Mysql setzt Boolesche Typoperationen

Mysql legt den Booleschen Typ fest 1. Tinyint-Typ...

Beispiel für die Konfiguration der Timeout-Einstellung für MySQL-Datenbanken

Inhaltsverzeichnis Vorwort 1. JDBC-Timeout-Einste...

Lösen Sie das Problem der Groß- und Kleinschreibung der Linux+Apache-Server-URL

Ich bin heute auf ein Problem gestoßen. Beim Eing...