HintergrundAls 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
Domänenübergreifende Backend-Konfigurationapp.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
Normaler Dateiupload hinteres EndeDas 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“ } }); FrontendHier 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
Hochladen großer DateienBeim 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
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 EndeDas Backend muss zwei Schnittstellen bereitstellen HochladenSpeichern 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()}`, }; } }); verschmelzenGemäß 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 herunterladenWenn 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); }); }; DateiidentifikationSie 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. 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:
|
<<: Tutorial zum Migrieren von Projekten von MYSQL zu MARIADB
>>: Detaillierte Erklärung der MySQL-Gruppensortierung, um die Top N zu finden
Inhaltsverzeichnis planen Abhängigkeiten installi...
Ohne weitere Umschweife werde ich den Code direkt...
Vorwort Wie wir alle wissen, unterstützt das auf ...
Ein Leser kontaktierte mich und fragte, warum es ...
Inhaltsverzeichnis So zeigen Sie den Quellcode de...
Inhaltsverzeichnis Was ist ein Index? Prinzip der...
Vorwort Heute entschied sich ein Kollege nach der...
In diesem Artikel wird der spezifische JS-Code zu...
Da jedermanns Zeit kostbar ist, werde ich die Pro...
Die Frontend-Entwicklungsabteilung wächst, die Mi...
Mysql legt den Booleschen Typ fest 1. Tinyint-Typ...
Wenn Sie das Idea-Entwicklungstool zum Debuggen v...
Da Springboot über einen integrierten Tomcat-Serv...
Inhaltsverzeichnis Vorwort 1. JDBC-Timeout-Einste...
Ich bin heute auf ein Problem gestoßen. Beim Eing...