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