Document upload forms

Keywords: ASP.NET JQuery IE Javascript network

As programmers, we often use the upload and download functions of files. When it's time to use it, check all kinds of information. There's wood. There's wood. In order to facilitate the next use, here is a summary and a memorandum.

Using Form to Upload Files

The most primitive, simple and rude file upload.
Front-end code:

//Mode 1
<form action="/Home/SaveFile1" method="post" enctype="multipart/form-data">
     <input type="file" class="file1" name="file1" />
     <button type="submit" class="but1">upload</button>
</form>

[Attention]

  • 1. Need post submission
  • 2. enctype="multipart/form-data" (transfer file)
  • 3. Form elements that need to be submitted need to set the name attribute

Background code:

public ActionResult SaveFile1()
{
    if (Request.Files.Count > 0)
    {
        Request.Files[0].SaveAs(Server.MapPath("~/App_Data/") + Request.Files[0].FileName);
        return Content("Successful Preservation");
    }
    return Content("No document read");
}

Form asynchronous upload (jquery.form plug-in)

Although the above approach is simple and crude, it is not friendly enough. Pages are bound to refresh. It is difficult to stay on the current page and give a hint of successful file upload.
With the passage of time, technology changes with each passing day. The emergence of ajax makes it easier to submit asynchronous files.
Next, we use the jquery.form plug-in to achieve asynchronous upload of files.
First we need to import jquery.js and jquery.form.js
Front-end code:

<form id="form2" action="/Home/SaveFile2" method="post" enctype="multipart/form-data">
    <input type="file" class="file1" name="file1" />
    <button type="submit" class="but1">Upload 1</button>
    <button type="button" class="but2">Upload 2</button>
</form>
//Mode 2 (Binding ajax operations through ajaxForm)
$(function () {
    $('#form2').ajaxForm({
        success: function (responseText) {
            alert(responseText);
        }
    });
});

//Mode 3 (performing ajax operations directly through ajaxSubmit)
$(function () {
    $(".but2").click(function () {
        $('#form2').ajaxSubmit({
            success: function (responseText) {
                alert(responseText);
            }
        });
    });
});

Background code:

public string SaveFile2()
{
    if (Request.Files.Count > 0)
    {                
        Request.Files[0].SaveAs(Server.MapPath("~/App_Data/") + Path.GetFileName(Request.Files[0].FileName));
        return "Successful Preservation";
    }
    return "No document read";
}

Principle:
Most of the time we use plug-ins, regardless of the other three, seven, twenty-one.
If you're curious, think about how this plug-in works. Look at the source code at more than 1500 lines. My god, it's just an asynchronous upload. Why is it so complicated?
It's hard to see what the hell is coming. Just debug the breakpoint.

The original plug-in has iframe and FormData uploaded in different ways to adapt to more versions of browsers.

Form Data

iframe is too disgusting. We see that we can use FormData to upload files, which is only available in Html 5. Let's try it ourselves.
Front-end code:

<input id="fileinfo" type="file" class="notFormFile" />
<button type="button" class="btnNotForm">Upload 4</button>
//Mode 4
$(".btnNotForm").click(function () {
    var formData = new FormData();//Initialize a FormData object
    formData.append("files", $(".notFormFile")[0].files[0]);//Squeeze files into FormData
    $.ajax({
        url: "/Home/SaveFile2",
        type: "POST",
        data: formData,
        processData: false,  // Tell jQuery not to process the data sent
        contentType: false,   // Tell jQuery not to set the Content-Type request header
        success: function (responseText) {
            alert(responseText);
        }
    });
});

After the code: (unchanged, or the example code)

public string SaveFile2()
{
    if (Request.Files.Count > 0)
    {                
        Request.Files[0].SaveAs(Server.MapPath("~/App_Data/") + Path.GetFileName(Request.Files[0].FileName));
        return "Successful Preservation";
    }
    return "No document read";
}

As we can see, the FormData object is only simulating data in an original form format. Is it possible to upload files using form or form format? The answer is yes. (I'll tell you right now)
Front-end code:

<input type="file"  id="file5" multiple>
<button type="button" class="btnFile5">Upload 5</button>    
//Mode 5
$(".btnFile5").click(function () {
    $.ajax({
        url: "/Home/SaveFile4",
        type: "POST",
        data: $("#file5")[0].files[0],
        processData: false,  // Tell jQuery not to process the data sent
        contentType: false,   // Tell jQuery not to set the Content-Type request header
        success: function (responseText) {
            alert(responseText);
        }
    });;       
});       
     

Background code:

public string SaveFile4()
{
    //It is found that only one network flow can be obtained, and there is no other information. (For example, file size, file format, file name, etc.)
    Request.SaveAs(Server.MapPath("~/App_Data/SaveFile4.data") + "", false);
    return "Successful Preservation";
}

Carefully you find that there is no form format, we can not tell the background other information (such as file format) besides uploading file stream data.
At this point, I seem to understand why uploaded files had to be wrapped in a form, which was originally only a transmission format agreed with the background.
In fact, when we simply use ajax of jq to transmit text data, we finally assemble data in form format, such as:

 $.ajax({
    data: { "userName": "Zhang San" } 

Partial upload

After knowing all the uploads above, are we satisfied with the status quo? no, many times we need to transfer large files. Generally, servers have certain size limitations.
One day, you found a passionate little movie that you wanted to share with everyone. Helplessly, the HD file is too big to transmit, what to do? We can turn the whole into zero, part of the transmission, that is, the so-called piecewise upload.
Front-end code:

<input type="file" id="file6" multiple>
<button type="button" class="btnFile6">Slice upload 6</button>
<div class="result"></div>
//Mode 6
 $(".btnFile6").click(function () { 
     var upload = function (file, skip) {
         var formData = new FormData();//Initialize a FormData object
         var blockSize = 1000000;//Size of each block
         var nextSize = Math.min((skip + 1) * blockSize, file.size);//Read to End Position             
         var fileData = file.slice(skip * blockSize, nextSize);//Intercept some file blocks
         formData.append("file", fileData);//Squeeze some files into FormData
         formData.append("fileName", file.name);//Save the file name
         $.ajax({
             url: "/Home/SaveFile6",
             type: "POST",
             data: formData,
             processData: false,  // Tell jQuery not to process the data sent
             contentType: false,   // Tell jQuery not to set the Content-Type request header
             success: function (responseText) {
                 $(".result").html("It has been uploaded." + (skip + 1) + "Block file");
                 if (file.size <= nextSize) {//If the upload is complete, jump out and continue uploading
                     alert("Upload completed");
                     return;
                 }
                 upload(file, ++skip);//Recursive call
             }
         });
     };

     var file = $("#file6")[0].files[0];
     upload(file, 0);
 }); 

Background code:

public string SaveFile6()
{
    //Save the file to the root directory App_Data+, get the file name and format
    var filePath = Server.MapPath("~/App_Data/") + Request.Form["fileName"];
    //Create a FileMode.Append file stream
    using (FileStream fs = new FileStream(filePath, FileMode.Append, FileAccess.Write))
    {
        using (BinaryWriter bw = new BinaryWriter(fs))
        {
            //Read File Stream
            BinaryReader br = new BinaryReader(Request.Files[0].InputStream);
            //Leave files in byte arrays
            byte[] bytes = br.ReadBytes((int)Request.Files[0].InputStream.Length);
            //Append byte arrays to files
            bw.Write(bytes);
        }
    }
    return "Successful Preservation";
}

Comparatively speaking, the amount of code is a little more and more complicated. However, it should be much simpler than other fragmented upload codes on the Internet (because there is no consideration of multi-file block upload at the same time, intermittent upload). In this way, you need to sort the file blocks in the background, then upload them and merge them in order, and then delete the original temporary files. Interested students can try it on their own, and it will be implemented later when analyzing the uploader plug-in.
Design sketch:

Description: What if we want to upload more than one file? In fact, H5 also provides a very simple way. Mark multiple directly in input, <input type= "file" id= "file6" multiple>, and then we receive an array Request.Files in the background.

Use HTML5 to drag, paste and upload

It can only be said that H5 is really powerful, more and more authority, more and more powerful operation.
Front-end code (drag-and-drop upload):

<textarea class="divFile7" style="min-width:800px;height:150px" placeholder="Please drag or paste the file directly here."></textarea>
//Mode 7
 $(".divFile7")[0].ondrop = function (event) {

     event.preventDefault();//Do not perform default actions associated with events
     var files = event.dataTransfer.files;//Get the dragged file

     //The following code remains unchanged
     var formData = new FormData();//Initialize a FormData object
     formData.append("files", files[0]);//Squeeze files into FormData
     $.ajax({
         url: "/Home/SaveFile2",
         type: "POST",
         data: formData,
         processData: false,  // Tell jQuery not to process the data sent
         contentType: false,   // Tell jQuery not to set the Content-Type request header
         success: function (responseText) {
             alert(responseText);
         }
     });
 };

Background code:
Slightly (like Save File 2 before)

Front-end code (paste upload limit picture format):

//Mode 8
$(".divFile7")[0].onpaste = function (event) {
    event.preventDefault();//Do not perform default actions associated with events
    var clipboard = event.clipboardData.items[0];//clipboard data
    if (clipboard.kind == 'file' || clipboard.type.indexOf('image') > -1) {//Judgment is picture format
        var imageFile = clipboard.getAsFile();//get files

        //The following code remains unchanged
        var formData = new FormData;
        formData.append('files', imageFile);
        formData.append('fileName', "temp.png");//Give the file a name here (or when it's saved directly in the background)
        $.ajax({
            url: "/Home/SaveFile8",
            type: "POST",
            data: formData,
            processData: false,  // Tell jQuery not to process the data sent
            contentType: false,   // Tell jQuery not to set the Content-Type request header
            success: function (responseText) {
                alert(responseText);
            }
        });
    }
};

Background code:

public string SaveFile8()
{
    //Save the file to the root directory App_Data+, get the file name and format
    var filePath = Server.MapPath("~/App_Data/") + Request.Form["fileName"];      
    if (Request.Files.Count > 0)
    {
        Request.Files[0].SaveAs(filePath);
        return "Successful Preservation";
    }
    return "No document read";
}

Design sketch:

Upload Plug-in (WebUploader)

I have listed and analyzed various ways of uploading files, and I think there is always one for you. However, the upload function is more general, and we may write a lot of cases without taking into account. Next, we briefly introduce Baidu's WebUploader plug-in.
Compared with our own simple upload, it has the advantages of stability, good compatibility (flash switching, so support IE), multi-function, concurrent upload, discontinuous upload (mainly backstage cooperation).
Official website: http://fex.baidu.com/webuploader/
Plug-in download: https://github.com/fex-team/webuploader/releases/download/0.1.5/webuploader-0.1.5.zip
Let's start using WebUploader
The first is simple and crude.
Front-end code:

<div id="picker">select file </div>
<button id="ctlBtn" class="btn btn-default">start uploading </button>
<!--Quote webuploader Of js and css-->
<link href="~/Scripts/webuploader-0.1.5/webuploader.css" rel="stylesheet" />
<script src="~/Scripts/webuploader-0.1.5/webuploader.js"></script>
<script type="text/javascript">
    var uploader = WebUploader.create({

        // (If it's a new browser, you don't need flash)
        //swf: '/Scripts/webuploader-0.1.5/Uploader.swf',

        // File receiving server.
        server: '/Webuploader/SaveFile',

        // Select the button for the file. Optional.
        // Internally, based on the current operation, it may be an input element or flash.
        pick: '#picker'
    });

    $("#ctlBtn").click(function () {
        uploader.upload();
    });

    uploader.on('uploadSuccess', function (file) {
        alert("Upload Success");
    });

</script>

Background code:

public string SaveFile()
{
    if (Request.Files.Count > 0)
    {
        Request.Files[0].SaveAs(Server.MapPath("~/App_Data/") + Path.GetFileName(Request.Files[0].FileName));
        return "Successful Preservation";
    }
    return "No document read";
}

Second, upload in segments. It's similar to what we wrote before.
Front-end code:

var uploader = WebUploader.create({ 
    //Compatible with older versions of IE
    swf: '/Scripts/webuploader-0.1.5/Uploader.swf', 
    // File receiving server.
    server: '/Webuploader/SveFile2', 
    // Start to upload fragments.
    chunked: true, 
    //Fragment size
    chunkSize: 1000000, 
    //Upload concurrency
    threads: 1,
    // Select the button for the file. 
    pick: '#picker'
});

// Click to trigger upload
$("#ctlBtn").click(function () {
    uploader.upload();
});

uploader.on('uploadSuccess', function (file) {
    alert("Upload Success");
});

Background code:

public string SveFile2()
{
    //Save the file to the root directory App_Data+, get the file name and format
    var filePath = Server.MapPath("~/App_Data/") + Path.GetFileName(Request.Files[0].FileName);
    //Create a FileMode.Append file stream
    using (FileStream fs = new FileStream(filePath, FileMode.Append, FileAccess.Write))
    {
        using (BinaryWriter bw = new BinaryWriter(fs))
        {
            //Read File Stream
            BinaryReader br = new BinaryReader(Request.Files[0].InputStream);
            //Leave files in byte arrays
            byte[] bytes = br.ReadBytes((int)Request.Files[0].InputStream.Length);
            //Append byte arrays to files
            bw.Write(bytes);
        }
    }
    return "Successful Preservation";
}

We see a parameter threads: 1 upload concurrent number, if we change to greater than 1 what will happen? The front end initiates multiple file uploads at the same time. Background will report errors, multiple processes operate a file at the same time.
So what if we want multithreaded uploads? Change the code (mainly background logic).
Front-end code:

//Concurrent upload (multithreaded upload)
var uploader = WebUploader.create({
    //Compatible with older versions of IE
    swf: '/Scripts/webuploader-0.1.5/Uploader.swf',
    // File receiving server.
    server: '/Webuploader/SveFile3',
    // Start to upload fragments.
    chunked: true,
    //Fragment size
    chunkSize: 1000000,
    //Upload concurrency
    threads: 10,
    // Select the button for the file.
    pick: '#picker'
});

// Click to trigger upload
$("#ctlBtn").click(function () {
    uploader.upload();
});

uploader.on('uploadSuccess', function (file) {
    //After the upload is completed, send a merge file command to the background
    $.ajax({
        url: "/Webuploader/FileMerge",
        data: { "fileName": file.name },
        type: "post",
        success: function () {
            alert("Upload Success");
        }
    });
});

Background code:

public string SveFile3()
{
    var chunk = Request.Form["chunk"];//How many movies are there at present? 

    var path = Server.MapPath("~/App_Data/") + Path.GetFileNameWithoutExtension(Request.Files
    if (!Directory.Exists(path))//Determine whether this path exists, and if it does not, create it
    {
        Directory.CreateDirectory(path);
    }
    //Save the file to the root directory App_Data+, get the file name and format
    var filePath = path + "/" + chunk;
    //Create a FileMode.Append file stream
    using (FileStream fs = new FileStream(filePath, FileMode.Append, FileAccess.Write))
    {
        using (BinaryWriter bw = new BinaryWriter(fs))
        {
            //Read File Stream
            BinaryReader br = new BinaryReader(Request.Files[0].InputStream);
            //Leave files in byte arrays
            byte[] bytes = br.ReadBytes((int)Request.Files[0].InputStream.Length);
            //Append byte arrays to files
            bw.Write(bytes);
        }
    }           
    return "Successful Preservation";
}
/// <summary>
/// Merge Documents
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public bool FileMerge()
{
    var fileName = Request.Form["fileName"];
    var path = Server.MapPath("~/App_Data/") + Path.GetFileNameWithoutExtension(fileName);

    //The sorting must be correct here, after converting to numbers (strings will be sorted by 11011, default 10 is smaller than 2)
    foreach (var filePath in Directory.GetFiles(path).OrderBy(t => int.Parse(Path.GetFileNameWithoutExtension(t))))
    {
        using (FileStream fs = new FileStream(Server.MapPath("~/App_Data/") + fileName, FileMode.Append, FileAccess.Write))
        {
            byte[] bytes = System.IO.File.ReadAllBytes(filePath);//Read files to byte arrays
            fs.Write(bytes, 0, bytes.Length);//write file
        }
        System.IO.File.Delete(filePath);
    }
    Directory.Delete(path);
    return true;
}

Do you think it's over here? Wrong, there are many other things that have not been taken into account. What happens if multiple users upload the same file name? How to achieve breakpoint continuation? Hasn't multiple files been selected yet? However, I don't intend to continue posting code here (more and more code will be added), so I'll practice it myself.
Provide a way to insert a data into the database before uploading. The data includes the path to be saved, the name of the file (named by GUID to prevent file conflicts with the same name), the file MD5 (used to identify the next continuation and seconds), the temporary file block storage path, and the complete success of file upload.
Then if we disconnect the network and then upload it, we first get the MD5 value of the file, and see if there are any files uploaded in the database, and if there are any, we can transfer them in seconds. If not, see if there are uploaded parts. If there are subsequent transfers, if not, a new file will be re-transferred.

summary

I had always wondered why uploading files must be wrapped in form, but now I understand.
When javascript wasn't popular at first, we could submit form data directly using the submit button. Forms can contain text and documents. Then with the popularity of JS and ajax, some form data can be submitted directly and asynchronously using ajax. I started to wonder why Ajax could submit its own assembled data. Then why can't you submit documents directly? I'm wrong here. The data submitted by AJAX is not random data, and finally it's assembled into form format (because background technology supports form format data more widely). However, the existing technology can not assemble a file format form data through js. Until the appearance of FormData in H5, it was possible for the front-end JS to assemble a form-formatted data containing files. So the form is just to satisfy the "agreed" data format with the background.

 

Relevant Recommendations

demo

Posted by mailtome on Tue, 11 Jun 2019 15:03:08 -0700