Source: Baoyu BLOG
1. I will explain the principle of component-less upload bit by bit with an example. The client HTML is as follows. To browse uploaded attachments, we pass the <input type="file"> element, but be sure to set the enctype attribute of the form to "multipart/form-data":
<form method="post" action="upload.asp" enctype="multipart/form-data">
<label>
<input type="file" name="file1" />
</label>
<br />
<input type="text" name="filename" value="default filename"/>
<br />
<input type="submit" value="Submit"/>
<input type="reset" value="Reset"/>
</form>
In the background asp program, it used to be very easy to obtain the ASCII data submitted by the form. But if you need to get the uploaded file, you must use the BinaryRead method of the Request object to read it. The BinaryRead method performs binary reading of a specified number of bytes from the current input stream. One thing to note is that once the BinaryRead method is used, the Request.Form or Request.QueryString collections can no longer be used. Combined with the TotalBytes property of the Request object, all data submitted by the form can be converted into binary, but these data are all encoded. First, let's take a look at how these data are encoded. Are there any rules to follow? Segment the code. In the code, we convert the binary read by BinaryRead into text and output it in the upload.asp in the background (note Do not upload large files in this example, otherwise the browser may crash):
<%
Dim biData, PostData
Size = Request.TotalBytes
biData = Request.BinaryRead(Size)
PostData = BinaryToString(biData,Size)
Response.Write "<pre>" & PostData & "</pre>" 'Use pre and output the format as is
'Convert the binary stream into text with the help of RecordSet
Function BinaryToString(biData,Size)
Const adLongVarChar = 201
Set RS = CreateObject("ADODB.Recordset")
RS.Fields.Append "mBinary", adLongVarChar, Size
RS.Open
RS.AddNew
RS("mBinary").AppendChunk(biData)
RS.Update
BinaryToString = RS("mBinary").Value
RS.Close
End Function
%>
For the sake of simplicity, upload the simplest text file (G:homepage.txt, with the content "Baoyu: http://www.webuc.net ") to test it. Keep the default value "default filename" in the text box filename. Submit and see the output:
--------------------------------7d429871607fe
Content-Disposition: form-data; name="file1"; filename="G:homepage.txt"
Content-Type: text/plain
Baoyu: http://www.webuc.net
--------------------------7d429871607fe
Content-Disposition: form-data; name="filename"
default filename
-----------------------------7d429871607fe--
It can be seen that for the items in the form, "----- --------------------------7d429871607fe" Such boundaries are used to separate pieces into pieces, and there is some description information at the beginning of each piece, for example: Content- Disposition: form-data; name="filename", in the description information, you can know the name of the form item through name="filename". If there is content like filename="G:homepage.txt", it means that it is an uploaded file. If it is an uploaded file, then the description information will have one more line Content-Type: text/plain to describe the Content-Type of the file. . Description information and body information are separated by line breaks.
Well, it's basically clear. According to this rule, we know how to separate the data and process the separated data. However, we almost overlooked one problem, which is the boundary value ("-------" in the above example) How did -----------------------7d429871607fe") know? This boundary value is different every time you upload it. Fortunately, it can be obtained through Request.ServerVariables("HTTP_CONTENT_TYPE") in asp. For example, in the above example, the content of HTTP_CONTENT_TYPE is: "multipart/form-data; boundary=-- --------------------------7d429871607fe", with this, we can not only determine whether enctype="multipart/form- data" (if it is not used, then there is no need to execute it below), you can also get the boundary value boundary=--------------------------- 7d429871607fe. (Note: The boundary value obtained here has less "--" at the beginning than the boundary value above. It is best to add it.)
As for the process of how to analyze the data, I will not go into details. It is nothing more than using functions such as InStr and Mid. to separate out the data we want.
2. When uploading in chunks, recording progress should reflect the progress bar in real time. The essence is to know how much data the current server has obtained in real time? Thinking back to the process of uploading, we implement it through Request.BinaryRead(Request.TotalBytes). During the Request process, we cannot know how much data the current server has obtained. So we can only use a workaround. If we can divide the obtained data into pieces, and then based on the number of blocks that have been uploaded, we can calculate how big it is currently uploaded! That is to say, if 1K is 1 block, then the input stream of uploading 1MB will be divided into 1024 blocks to obtain. For example, if I have obtained 100 blocks, it means that 100K has been uploaded. When I proposed blocking, many people found it incredible because they ignored that the BinaryRead method can not only read the specified size, but also read continuously.
Write an example to verify the integrity of block reading, based on the example just now (note that this example does not upload large files, otherwise it may cause the browser to crash):
<%
Dim biData, PostData, TotalBytes, ChunkBytes
ChunkBytes = 1 * 1024 ' Chunk size is 1K
TotalBytes = Request.TotalBytes 'Total size
PostData = "" ' Data converted to text type
ReadedBytes = 0 'Initialized to 0
'Read in chunks
Do While ReadedBytes < TotalBytes
biData = Request.BinaryRead(ChunkBytes) 'Current chunk
PostData = PostData & BinaryToString(biData,ChunkBytes) 'Convert the current chunk to text and splice it
ReadedBytes = ReadedBytes + ChunkBytes 'Record read size
If ReadedBytes > TotalBytes Then ReadedBytes = TotalBytes
Loop
Response.Write "<pre>" & PostData & "</pre>" ' Use pre and output the format as is
' Convert binary stream into text
Function BinaryToString(biData,Size)
Const adLongVarChar = 201
Set RS = CreateObject("ADODB.Recordset")
RS.Fields.Append "mBinary", adLongVarChar, Size
RS.Open
RS.AddNew
RS("mBinary").AppendChunk(biData)
RS.Update
BinaryToString = RS("mBinary").Value
RS.Close
End Function
%>
Try uploading the text file just now. The output results prove that the content read in blocks is complete, and in the While loop, we can record the current status into the Application every time it loops, and then we can pass Access the Application to dynamically obtain the upload progress bar.
Also: In the above example, string splicing is used. If you want to splice binary data, you can use the Write method of the ADODB.Stream object. The sample code is as follows:
Set bSourceData = createobject("ADODB.Stream")
bSourceData.Open
bSourceData.Type = 1 'Binary
Do While ReadedBytes < TotalBytes
biData = Request.BinaryRead(ChunkBytes)
bSourceData.Write biData ' Directly use the write method to write the current file stream into bSourceData
ReadedBytes = ReadedBytes + ChunkBytes
If ReadedBytes > TotalBytes Then ReadedBytes = TotalBytes
Application("ReadedBytes") = ReadedBytes
Loop
3. Save the uploaded file. Obtain the submitted data through Request.BinaryRead. After separating the uploaded file, the saving method is different depending on the data type:
for binary data, you can save the binary stream directly through the SaveToFile method of the ADODB.Stream object. Become a file.
For text data, you can save the text data to a file through the Write method of the TextStream object.
Text data and binary data can be easily converted to each other. For uploading small files, there is basically no difference between the two. However, there are still some differences when saving between the two methods. For the ADODB.Stream object, all data must be loaded before it can be saved as a file. Therefore, uploading large files using this method will occupy a lot of memory, and for the TextStream object, After the file is created, you can write part of it at a time and write it multiple times. The advantage of this is that it will not occupy the server memory space. Combined with the principle of obtaining data in blocks analyzed above, we can write each piece of uploaded data to the file. middle. I once did an experiment and uploaded a file of more than 200 MB to the same machine. Using the first method, the memory kept increasing. In the end, it directly prompted that the computer's virtual memory was insufficient. The most annoying thing is that even though the progress bar indicates that the file has been uploaded, in the end, The file is still not saved. Using the latter method, there is basically no change in the memory during the upload process.
4. Unsolved problems I saw Bestcomy on the blog describing that his Asp.Net upload component can be independent of Sever.SetTimeOut, but in Asp I was not able to do it. For uploading large files, I can only use Server.SetTimeOut can be set to a very large value. I don't know if there is a better solution.
If we use the Write method of the TextStream object when saving a file, then if the user interrupts the file transfer when uploading, the part of the file that has been uploaded is still there. It would be great if the upload could be resumed. The key problem is that although the Request.BinaryRead method can read in blocks, it cannot skip a certain section of reading!
5. Conclusion The principle is basically explained clearly, but the actual code is much more complicated than this. There are many issues to consider. The most troublesome part is the analysis of the data. For each piece of acquired data, it is necessary to analyze whether it belongs to the description information. The form item is still an uploaded file, and whether the file has been uploaded...
I believe that based on the above description, you can also develop your own powerful component-less upload component. I think more people only care about the code and don't know how to write it themselves. Maybe they don't have the time, maybe the level is not enough, and more of it has just become a habit... I have seen too many technologies on CSDN Eight-part essay - a paragraph of description, and then all the code. Teaching a man to fish is not as good as teaching him to fish. If I give you a code, you may not think about why and just use it. When you encounter a similar problem next time, you still don’t know why. I hope this article can help More people learn something, and the most important thing is to "enlighten" something!