Ich habe schon lange vom Hochladen von Lebensläufen mit Haltepunkten gehört, und das Frontend kann es auch implementieren. Die Implementierung des Herunterladens von Lebensläufen über Haltepunkte im Frontend basiert hauptsächlich auf den neuen Funktionen von HTML5, sodass die Unterstützung älterer Browser im Allgemeinen nicht hoch ist.
In diesem Artikel wird ein einfaches Beispiel für die Wiederaufnahme eines Haltepunkts (Front-End-Dateiübermittlung + Back-End-PHP-Dateiempfang) verwendet, um den allgemeinen Implementierungsprozess zu verstehen.
Nehmen wir zunächst das Bild als Beispiel Werfen Sie einen Blick auf das Finale. Sieht aus wie
Einige Wissensvorbereitungen
Setzen Sie die Übertragung am Haltepunkt fort Es gibt eine Pause, es sollten Dateien vorhanden sein. Der Prozess der Segmentierung wird Stück für Stück weitergegeben.
In der Vergangenheit konnten Dateien nicht geteilt werden, aber mit der Einführung neuer Funktionen von HTML5, ähnlich der Aufteilung gewöhnlicher Zeichenfolgen und Arrays, können wir die Slice-Methode zum Aufteilen von Dateien verwenden.
Die grundlegendste Implementierung der Haltepunktwiederaufnahme ist also: Das Front-End ruft die entsprechende Datei über das FileList-Objekt ab, teilt die große Datei gemäß der angegebenen Teilungsmethode in Segmente auf und übergibt sie dann an das Back-End-Teil Fügen Sie dann die Dateien Stück für Stück zusammen.
Wir müssen das FileList-Objekt ändern und es dann senden. Im vorherigen Artikel haben wir einige Punkte gelernt, die bei dieser Art der Übermittlung zu beachten sind. Da das FileList-Objekt nicht direkt geändert werden kann, kann es nicht direkt weitergeleitet werden Zum Hochladen und Senden müssen Sie das FormData-Objekt kombinieren, um neue Daten zu generieren, und den Upload-Vorgang über Ajax durchführen.
2. Implementierungsprozess
Dieses Beispiel implementiert die Grundfunktion zum Fortsetzen des Datei-Uploads an Haltepunkten. Der manuelle Vorgang „Upload anhalten“ war jedoch nicht erfolgreich Upload-Prozess. Simulieren Sie die Unterbrechung des Uploads und erleben Sie „Fortsetzung des Uploads“.
1. Front-End-Implementierung
Wählen Sie zuerst die Datei aus, listen Sie die ausgewählten Dateilisteninformationen auf und passen Sie dann den Upload-Vorgang an
(1) Richten Sie also die Seite ein erste DOM-Struktur
<!-- 上传的表单 --> <form method="post" id="myForm" action="/fileTest.php" enctype="multipart/form-data"> <input type="file" id="myFile" multiple> <!-- 上传的文件列表 --> <table id="upload-list"> <thead> <tr> <th width="35%">文件名</th> <th width="15%">文件类型</th> <th width="15%">文件大小</th> <th width="20%">上传进度</th> <th width="15%"> <input type="button" id="upload-all-btn" value="全部上传"> </th> </tr> </thead> <tbody> </tbody> </table> </form> <!-- 上传文件列表中每个文件的信息模版 --> <script type="text/template" id="file-upload-tpl"> <tr> <td>{{fileName}}</td> <td>{{fileType}}</td> <td>{{fileSize}}</td> <td class="upload-progress">{{progress}}</td> <td> <input type="button" class="upload-item-btn" data-name="{{fileName}}" data-size="{{totalSize}}" data-state="default" value="{{uploadVal}}"> </td> </tr> </script>
Wirf die CSS-Stile hier weg
<style type="text/css"> body { font-family: Arial; } form { margin: 50px auto; width: 600px; } input[type="button"] { cursor: pointer; } table { display: none; margin-top: 15px; border: 1px solid #ddd; border-collapse: collapse; } table th { color: #666; } table td, table th { padding: 5px; border: 1px solid #ddd; text-align: center; font-size: 14px; } </style>
(2) Als nächstes folgt die JS-Implementierungsanalyse
Wir können einige Informationen über die Datei über das FileList-Objekt erhalten
Die Größe ist die Dateigröße, Die Dateiaufteilung muss sich darauf stützen
Die Größe hier ist die Anzahl der Bytes. Wenn die Schnittstelle also die Dateigröße anzeigt, kann sie wie folgt konvertiert werden
// 计算文件大小 size = file.size > 1024 ? file.size / 1024 > 1024 ? file.size / (1024 * 1024) > 1024 ? (file.size / (1024 * 1024 * 1024)).toFixed(2) + 'GB' : (file.size / (1024 * 1024)).toFixed(2) + 'MB' : (file.size / 1024).toFixed(2) + 'KB' : (file.size).toFixed(2) + 'B';
Nachdem Sie die Datei ausgewählt haben, zeigen Sie die Dateiinformationen an und ersetzen Sie die Daten in der Vorlage
// 更新文件信息列表 uploadItem.push(uploadItemTpl .replace(/{{fileName}}/g, file.name) .replace('{{fileType}}', file.type || file.name.match(/\.\w+$/) + '文件') .replace('{{fileSize}}', size) .replace('{{progress}}', progress) .replace('{{totalSize}}', file.size) .replace('{{uploadVal}}', uploadVal) );
Bei der Anzeige der Dateiinformationen: Möglicherweise wurde diese Datei schon einmal hochgeladen. Um den Upload an einem Haltepunkt fortzusetzen, muss er auf der Schnittstelle beurteilt und aufgefordert werden.
Überprüfen Sie den lokalen Bereich, um zu sehen, ob entsprechende Daten vorhanden sind (der Ansatz hier besteht darin, dass, wenn der lokale Datensatz zu 100 % hochgeladen wurde, dieser direkt erneut hochgeladen wird, anstatt mit dem Hochladen fortzufahren)
// 初始通过本地记录,判断该文件是否曾经上传过 percent = window.localStorage.getItem(file.name + '_p'); if (percent && percent !== '100.0') { progress = '已上传 ' + percent + '%'; uploadVal = '继续上传'; }
Zeigt die Dateiinformationsliste an
Klicken Sie, um mit dem Hochladen zu beginnen, und Sie können die entsprechende Datei hochladen
Beim Hochladen einer Datei müssen Sie die Datei in Segmente aufteilen.
Zum Beispiel ist jedes hier konfigurierte Segment 1024B, das gesamte Chunk-Segment (wird verwendet, um zu bestimmen, ob es das letzte Segment ist), das Chunk-Segment, der aktuell hochgeladene Prozentsatz usw.
Was erwähnt werden muss, ist der Vorgang des Anhaltens des Uploads. Ich habe es tatsächlich noch nicht implementiert, aber ich habe keine andere Wahl...Der nächste Schritt ist der Segmentierungsprozess
// 设置分片的开始结尾 var blobFrom = chunk * eachSize, // 分段开始 blobTo = (chunk + 1) * eachSize > totalSize ? totalSize : (chunk + 1) * eachSize, // 分段结尾 percent = (100 * blobTo / totalSize).toFixed(1), // 已上传的百分比 timeout = 5000, // 超时时间 fd = new FormData($('#myForm')[0]); fd.append('theFile', findTheFile(fileName).slice(blobFrom, blobTo)); // 分好段的文件 fd.append('fileName', fileName); // 文件名 fd.append('totalSize', totalSize); // 文件总大小 fd.append('isLastChunk', isLastChunk); // 是否为末段 fd.append('isFirstUpload', times === 'first' ? 1 : 0); // 是否是第一段(第一次上传) // 上传之前查询是否以及上传过分片 chunk = window.localStorage.getItem(fileName + '_chunk') || 0; chunk = parseInt(chunk, 10);
文件应该支持覆盖上传,所以如果文件以及上传完了,现在再上传,应该重置数据以支持覆盖(不然后端就直接追加 blob数据了)
// 如果第一次上传就为末分片,即文件已经上传完成,则重新覆盖上传 if (times === 'first' && isLastChunk === 1) { window.localStorage.setItem(fileName + '_chunk', 0); chunk = 0; isLastChunk = 0; }
这个 times 其实就是个参数,因为要在上一分段传完之后再传下一分段,所以这里的做法是在回调中继续调用这个上传操作
接下来就是真正的文件上传操作了,用Ajax上传,因为用到了FormData对象,所以不要忘了在$.ajax({}加上这个配置processData: false
上传了一个分段,通过返回的结果判断是否上传完毕,是否继续上传
success: function(rs) { rs = JSON.parse(rs); // 上传成功 if (rs.status === 200) { // 记录已经上传的百分比 window.localStorage.setItem(fileName + '_p', percent); // 已经上传完毕 if (chunk === (chunks - 1)) { $progress.text(msg['done']); $this.val('已经上传').prop('disabled', true).css('cursor', 'not-allowed'); if (!$('#upload-list').find('.upload-item-btn:not(:disabled)').length) { $('#upload-all-btn').val('已经上传').prop('disabled', true).css('cursor', 'not-allowed'); } } else { // 记录已经上传的分片 window.localStorage.setItem(fileName + '_chunk', ++chunk); $progress.text(msg['in'] + percent + '%'); // 这样设置可以暂停,但点击后动态的设置就暂停不了.. // if (chunk == 10) { // isPaused = 1; // } console.log(isPaused); if (!isPaused) { startUpload(); } } } // 上传失败,上传失败分很多种情况,具体按实际来设置 else if (rs.status === 500) { $progress.text(msg['failed']); } }, error: function() { $progress.text(msg['failed']); }
2. 后端实现
要注意一下,通过FormData对象上传的文件对象,在PHP中也是通过$_FILES全局对象获取的,还有为了避免上传后文件中文的乱码,用一下iconv
断点续传支持文件的覆盖,所以如果已经存在完整的文件,就将其删除
// 如果第一次上传的时候,该文件已经存在,则删除文件重新上传 if ($isFirstUpload == '1' && file_exists('upload/'. $fileName) && filesize('upload/'. $fileName) == $totalSize) { unlink('upload/'. $fileName); }
使用上述的两个方法,进行文件信息的追加,别忘了加上 FILE_APPEND 这个参数~
// 继续追加文件数据 if (!file_put_contents('upload/'. $fileName, file_get_contents($_FILES['theFile']['tmp_name']), FILE_APPEND)) { $status = 501; } else { // 在上传的最后片段时,检测文件是否完整(大小是否一致) if ($isLastChunk === '1') { if (filesize('upload/'. $fileName) == $totalSize) { $status = 200; } else { $status = 502; } } else { $status = 200; } }
一般在传完后都需要进行文件的校验吧,所以这里简单校验了文件大小是否一致。
根据实际需求的不同有不同的错误处理方法,这里就先不多处理了
完整的PHP部分
0) { $status = 500; } else { // 此处为一般的文件上传操作 // if (!move_uploaded_file($_FILES['theFile']['tmp_name'], 'upload/'. $_FILES['theFile']['name'])) { // $status = 501; // } else { // $status = 200; // } // 以下部分为文件断点续传操作 // 如果第一次上传的时候,该文件已经存在,则删除文件重新上传 if ($isFirstUpload == '1' && file_exists('upload/'. $fileName) && filesize('upload/'. $fileName) == $totalSize) { unlink('upload/'. $fileName); } // 否则继续追加文件数据 if (!file_put_contents('upload/'. $fileName, file_get_contents($_FILES['theFile']['tmp_name']), FILE_APPEND)) { $status = 501; } else { // 在上传的最后片段时,检测文件是否完整(大小是否一致) if ($isLastChunk === '1') { if (filesize('upload/'. $fileName) == $totalSize) { $status = 200; } else { $status = 502; } } else { $status = 200; } } } echo json_encode(array( 'status' => $status, 'totalSize' => filesize('upload/'. $fileName), 'isLastChunk' => $isLastChunk )); ?>
更多Das Front-End implementiert den Haltepunkt zum Fortsetzen des Hochladens von Dateien (Front-End-Dateiübermittlung + Back-End-PHP-Dateiempfang).相关文章请关注PHP中文网!