I’ve heard of breakpoint continuation for a long time, and the front end can implement it
The front-end implementation of breakpoint continuation relies on the new features of HTML5, so it is generally not well supported on older browsers
This article uses a simple example of breakpoint continuation (front-end file commit + back-end PHP file receive) to understand the general implementation process
Let’s take a picture and see what it looks like at the end
First, some knowledge preparation
Breakpoint continued, since there is a break, it should have a file segmentation process, a section of the passage.
In the past, files could not be split, but with the introduction of HTML5 new features, such as ordinary string and array segmentation, we can use the slice method to split files.
Therefore, the most basic implementation of breakpoint continuation is: the front end obtains the corresponding file through the FileList object, divides the large file into sections according to the specified method, and then passes the file section by section to the back end. The back end then splices the file section by section in sequence.
The FileList object cannot be changed directly, so it cannot be submitted directly through the.submit() method of the form. You need to combine the FormData object with a list of names to create a new one. Upload via Ajax.
Second, the implementation process
This example realizes the basic function of file breakpoint resume, but the manual “pause upload” operation has not been successful, you can refresh the page during the upload process to simulate the interruption of upload, experience “breakpoint resume”,
There may be other minor bugs, but the basic logic is pretty much the same.
1. Front-end implementation
First select the file, list the selected file list information, and then customize the upload operation
(1) Set the page DOM structure first
The file name
The file type
The file size
Upload progress
Copy the code
I’m going to throw out the CSS style as well
View Code
(2) Next is the implementation of JS analysis
We can get some information about a file from a FileList object
Size is the size of the file, and the file is divided and sharded depending on this
Here size is the number of bytes, so when the screen shows the size of the file, it can be converted like this
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';Copy the code
Select the file and display the information for the file. Replace the data in the template
Uploaditem.push (uploadItemtpl. replace(/{{fileName}}/g, file.name).replace('{{fileType}}', File. Type | | file. Name. Match (. / \ \ w + $/) + 'files'). The replace (' {{fileSize}}, size). The replace (' {{progress}}, progress) .replace('{{totalSize}}', file.size) .replace('{{uploadVal}}', uploadVal) );Copy the code
However, when displaying the file information, the file may have been uploaded before, in order to continue the breakpoint, you need to determine and make a prompt on the interface
Query local data to see if there is corresponding data (the practice here is that when the local record is 100% uploaded, it is directly re-uploaded instead of continuing to upload)
/ / initial through local records, and determine whether the file has been uploaded percent = window. The localStorage. The getItem (file. The name + '_p'); if (percent && percent ! == '%') {progress = '%' + '%'; UploadVal = 'upload '; }Copy the code
Displays a list of file information
Click Start upload, you can upload the corresponding file
When uploading a file, you need to fragment the file
For example, each 1024B is configured here, the total chunks are used to determine whether the chunks are the last chunks, the chunk is the first chunk, and the percentage of the uploaded chunks
It should be mentioned that the operation of suspending uploading is not implemented yet, so I have no choice but to pause ing…
Next comes the segmentation process
Var blobFrom = chunk * eachSize, blobTo = (chunk + 1) * eachSize > totalSize? totalSize : (chunk + 1) * eachSize, // End of segment percent = (100 * blobTo/totalSize). ToFixed (1), // End of segment percent = (100 * blobTo/totalSize). $('#myForm')[0]); fd.append('theFile', findTheFile(fileName).slice(blobFrom, blobTo)); Fd. append('fileName', fileName); // file fd.append('totalSize', totalSize); Fd.append ('isLastChunk', isLastChunk); Fd. append('isFirstUpload', times === 'first'? 1:0); // Is it the first paragraph (the first upload)Copy the code
/ / upload before query whether and upload excessive piece the chunk = window. The localStorage. The getItem (fileName + '_chunk') | | 0; chunk = parseInt(chunk, 10);Copy the code
The file should support overwrite uploads, so if the file has been uploaded and is now uploaded, the data should be reset to support overwrite.
// If the first upload is final, that is, the file has been uploaded. The back cover to upload the if (times = = = 'first' && isLastChunk = = = 1) {window. LocalStorage. SetItem (fileName + '_chunk', 0); chunk = 0; isLastChunk = 0; }Copy the code
The Times is just an argument, because the previous section is passed before the next section is passed, so the idea here is to call the upload operation again in the callback
The next step is to actually upload the file, using Ajax, because you’re using the FormData object, so don’t forget to add the processData: false configuration to $.ajax({}
A section has been uploaded, and the returned result is used to determine whether the upload is complete and whether to continue uploading
success: function(rs) { rs = JSON.parse(rs); / / upload success if (rs) status = = = 200) {/ / record already upload window. The percentage of localStorage. SetItem (fileName + '_p', percent); If (chunk === (chunk-1)) {$progress.text(MSG ['done']); $this. Val (' have uploaded). Prop (" disabled ", true). CSS (' cursor ', 'not - charges'); if (! $(' # upload - the list). The find ('. The upload - item - BTN: not (disabled) '). The length) {$(' # upload - all - BTN). Val (' have uploaded). Prop (" disabled ", true).css('cursor', 'not-allowed'); }} else {/ / window. The record has been uploaded divided localStorage. SetItem (fileName + '_chunk', + + the chunk); $progress.text(msg['in'] + percent + '%'); // This setting can be paused, but the dynamic setting can not be paused after clicking on it.. // 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']); }Copy the code
As the upload of the next segment continues, a recursive operation is performed, uploading the next segment in sequence
Cut a figure..
This is a complete JS logic, code a little annotation should not be difficult to understand it haha
View Code
2. Back-end implementation
The back-end implementation here is relatively simple and relies on file_put_contents and file_get_contents
Note that file objects uploaded via FormData are also retrieved from the $_FILES global object in PHP, and iconv is used to avoid garble in uploaded files
Breakpoint continuation supports file overwriting, so if a full file already exists, delete it
// If the file already exists when you first upload it, If ($isFirstUpload == '1' && file_exists('upload/'. $fileName) && filesize('upload/'. $fileName) == $totalSize) { unlink('upload/'. $fileName); }Copy the code
Use the above two methods to append file information, and don’t forget to add the FILE_APPEND parameter ~
// Continue appending file data if (! file_put_contents('upload/'. $fileName, file_get_contents($_FILES['theFile']['tmp_name']), FILE_APPEND)) { $status = 501; } else {// When uploading the last fragment, If ($isLastChunk === '1') {if (filesize('upload/'. $fileName) == $totalSize) {$status = 200; } else { $status = 502; } } else { $status = 200; }}Copy the code
Generally, after the transfer, you need to verify the file, so here is a simple check whether the file size is consistent
There are different error handling methods based on actual requirements, but I won’t deal with them here
The complete PHP section
0) { $status = 500; } else {// This is the general file upload operation // if (! move_uploaded_file($_FILES['theFile']['tmp_name'], 'upload/'. $_FILES['theFile']['name'])) { // $status = 501; // } else { // $status = 200; // If the file already exists at the time of the first upload, If ($isFirstUpload == '1' && file_exists('upload/'. $fileName) && filesize('upload/'. $fileName) == $totalSize) { unlink('upload/'. $fileName); } // Otherwise continue appending file data if (! file_put_contents('upload/'. $fileName, file_get_contents($_FILES['theFile']['tmp_name']), FILE_APPEND)) { $status = 501; } else {// When uploading the last fragment, 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 )); ? >Copy the code
Let’s stop here