I have gone through the internet and my email trying to collect many of the questions that I have received in the past concerning version 1 and 2 of the Upload Files without COM scripts. I have also tried to display examples for common scenarios that many of you will use. Please read through this FAQ fully before sending me a question.
General InformationThis script allows you to upload multiple files and fields without purchasing or registering COM objects. It is ideal for shared hosts that frown upon 3rd Party com objects or charge an arm and a leg for them.
This script provides an object oriented interface using classes for easier development. The power that this script has over others is that it does not use dictionary objects to store the uploaded files. It also harnesses the power of ADODB to prepare and save binary data to the file system with quick speeds. This is the script that you have been looking for. You can get a really good feel for how the script is used just by reading through this FAQ.
The code is commented very well and comes along with examples of how to do common operations, as well as the FAQ you are reading now. Bugs from version 2 have been fixed, or otherwise throw exceptions to the user giving them details of how to correct the problem (such as giving permission to the internet user account). Memory is managed more efficiently in version 3 that allows for more scalability and larger files.
You should have Microsoft Data Access Components (ADODB) 2.6 or later installed. Also, you need to have Microsoft Windows Script 5.5 or later installed as well. Many ISP's have this installed by default. If your ISP does not have them installed, you can direct them to download the software from Microsoft's website for free.
Microsoft Data Access Components
Microsoft Windows Script
I would also like to point out that you must run this script on a website that can process ASP scripts. You can not view these scripts from your file system. You will have to type in a URL in your web browser beginning with http://. Microsoft provides a web server called Internet Information Server (IIS) for servers and Personal Web Server for personal computers. Personal Web Server was made available with Windows NT 4.0 Option Pack, but is compatible with Windows 9x operating systems.
The script is free to use within your website. I do ask that you register this script at http://upload.lewismoten.com. Monetary donations (large and small) are accepted through PayPal with my account, Axiom Studios, Inc. If you would like to donate equipment, hosting space, or software, please contact me at lewis@moten.com.
Yes, you may modify it for your own website, but you may not publish or distribute the code without my written permission. Simply put, if you are making money with my script, I would appreciate some acknowledgement, funds, hosted space and/or hardware to encourage my development for the open source community. Email me for more details.
I am aware that since version 2, I have used the MDAC components (ADODB) to speed up the upload process. If you want to get technical, using the Request object is a COM component. I could rename this script to upload files without having to install additional COM on most current standard windows operating systems. Instead, I kept the name simple and to the point. It is the first thing people start looking for when they need the solution that I provide.
Microsoft Internet Explorer 6.0 appears to work fine on the Windows Platform.
Mozilla 1.4 works ok on Windows. I have had problems when dealing with Unicode.
I have heard that users with Macintosh computers have problems. If someone has a spare Mac that they would like to donate, then I would be willing to take the time to fix up the script to support them.
These instructions are written for those of you who have access to the computer with a default installation of IIS. Some steps may have to be changed according to your setup.
The upload script depends on three different scripts. These are clsField.asp, clsUpload.asp, and clsProgress.asp. In your website, do the following:
<FORM method="post" encType="multipart/form-data" action="Upload.asp">
<INPUT type="File" name="File1">
<INPUT type="Submit" value="Upload">
</FORM>
<!--#INCLUDE FILE="clsUpload.asp"-->
<%
Dim Upload
Dim Folder
Set Upload = New clsUpload
Folder = Server.MapPath("Uploads") & "\"
Upload("File1").SaveAs Folder & Upload("File1").FileName
Set Upload = Nothing
%>
Hopefully, everything worked fine and you are on your way. Some of the steps may need to be changed, according to the location of your website.
Open up your file explorer and locate the folder that you wish to update files to. Right mouse click the folder and click Properties. Click the Security tab. You will see a list of users who have access to the folder. Click the Add button and locate a user beginning with "IUSR_", it will be followed by the name of your computer. This is called the Internet Guest Account. When you click on the user in the list of assigned users, you will need to verify that they have access to write, modify and read.
Add additional INPUT tags to your form for uploading files. Give each one a different name, such as File1, File2, etc.
<INPUT type="file" name="File1">
<INPUT type="file" name="File2">
<INPUT type="file" name="File3">
Dim Upload
Dim FileName
Dim Folder
Set Upload = New clsUpload
Folder = Server.MapPath("Uploads") & "\"
Upload("File1").SaveAs Folder & Upload("File1").FileName
Upload("File2").SaveAs Folder & Upload("File2").FileName
Upload("File3").SaveAs Folder & Upload("File3").FileName
Set Upload = Nothing
You can add additional input elements that are different types.
<INPUT type="file" name="File1">
<INPUT type="text" name="Description">
Dim Upload
Dim Description
Dim FileName
Dim Folder
Set Upload = New clsUpload
Description = Upload("Description").Value
FileName = Upload("File1").FileName
Folder = Server.MapPath("Uploads") & "\"
Upload("File1").SaveAs Folder & FileName
Set Upload = Nothing
Response.Write "Description: " & Description
The progress meter is a little tricky, but I have done most of the work for you in determining how it can be done. When the user clicks the Submit button, you will need to open a Popup window that will display the status for the current upload session. Sessions are identified by passing a query string to both the Popup window, and the ASP file that processes the upload. This session should be a random number, or you will run the risk of your users seeing progress for someone else's upload.
One of the most important rules for the progress window is that you must disable session state for the ASP page. You can leave session state active for the rest of your website, but this page can not use it. If session state is enabled, then your progress window will appear to hang until the upload has completed. To disable session state in one page, you can use the following tag at the top of your page: <%@ EnableSessionState=False %>
<FORM onsubmit="return UploadForm_submit()" method="post" action="Upload.asp" name="UploadForm" encType="multipart/form-data">
<INPUT type="file" name="File1">
<INPUT type="submit" value="Upload">
</FORM>
<SCRIPT language="javascript">
function UploadForm_submit()
{
// Create a random session identifier.
var Session = new String();
Session = Math.floor(Math.random() * 0xFFFFFF).toString(16);
// append identification of session
document.UploadForm.action += "?Session=" + Session;
// open the pop-up window
window.open("Status.asp?Session=" + Session ,"upload","width=200,height=200");
}
</SCRIPT>
<%@EnableSessionState=False%>
<!--#INCLUDEFILE= "clsProgress.asp"-->< BR> <%
Dim Progress
Set Progress = New clsProgress
Progress.Load
Response.Write "Uploaded "
Response.Write Progress.BytesReceived
Response.Write " of "
Response.Write Progress.TotalBytes
Set Progress = Nothing
%>
<SCRIPT language="javascript">
// reload this page after 1 second.
window.setTimeout("window.location.reload()", 1000);
</SCRIPT>
The progress class exposes the basic information about your upload session. View the structure documentation for a list of all of its methods and properties. With this information, you can create your own progress meters. An example progress meter has been included for you with the original script package called Progress_Status.asp.
A collection is sent when you have a select list that allows for more then one item to be chosen, or if you have multiple fields with the same name on your form. In the past, this was a little tricky to access, but now a new feature has been introduced. You can access an array of fields by calling the Collection method on the Upload object. Each field of the form is sent, in the order in which it occurs in the form.
<SELECT multiple size="3" name="Colors">
<OPTION selected value="#ff0000">Red
<OPTION selected value="#ffffff">White
<OPTION value="#0000ff">Blue
</SELECT>
Dim Upload
Set Upload = New clsUpload
Dim Colors
Dim Color
Colors = Upload.Collection("Colors")
For Each Color In Colors
Response.Write Color.Value & "<BR>"
Next
Set Upload = Nothing
The upload object has a routine called DeleteFile. Pass the full path to the file to this routine.
Dim Upload
Dim FileName
Dim Folder
Set Upload = New clsUpload
FileName = Upload("File1").FileName
Folder = Server.MapPath("Uploads") & "\"
Upload.DeleteFile Folder & FileName
Upload("File1").SaveAs Folder & FileName
Set Upload = Nothing
The upload object has a routine called RenameFile. Pass the full path to the file to this routine, and the new name of the file.
Dim Upload
Dim FileName
Dim Folder
Set Upload = New clsUpload
FileName = Upload("File1").FileName
Folder = Server.MapPath("Uploads") & "\"
Upload.RenameFile Folder & FileName, "NewFileName.gif"
Upload("File1").SaveAs Folder & FileName
Set Upload = Nothing
The upload object has a routine called CopyFile. Pass the full path of the current file to this routine, and the full path to the target location.
Dim Upload
Dim FileName
Dim Folder
Set Upload = New clsUpload
FileName = Upload("File1").FileName
Folder = Server.MapPath("Uploads") & "\"
Upload.CopyFile Folder & FileName, Folder & "NewFileName.gif"
Upload("File1").SaveAs Folder & FileName
Set Upload = Nothing
The upload object has a routine called MoveFile. Pass the full path of the current file to this routine, and the full path to the target location.
Dim Upload
Dim FileName
Dim Folder
Set Upload = New clsUpload
FileName = Upload("File1").FileName
Folder = Server.MapPath("Uploads") & "\"
Upload.MoveFile Folder & FileName, Folder & "NewFileName.gif"
Upload("File1").SaveAs Folder & FileName
Set Upload = Nothing
The field object has a property called FileExt. You can check this to verify that the file extension is valid, or a potential risk.
Dim Upload
Dim FileName
Dim Folder
Dim Ext
Set Upload = New clsUpload
Ext = Upload("File1").FileExt
Select Case Ext
Case "GIF", "JPG", "PNG", "BMP"
FileName = Upload("File1").FileName
Folder = Server.MapPath("Uploads") & "\"
Upload("File1").SaveAs Folder & FileName
Case Else
Response.Write "File type not supported."
End Select
Set Upload = Nothing
Although a filenames extension may be valid, it may still contain harmful information. Imagine a scenario where a user creates a virus and renames it to photo.gif. This will get past your file extension validation. You may wish to use other scripts or components to validate the binary data as well. You can use another script I crated called Read Image Dimensions to help aid you in verifying a files actual content when dealing with images. The class clsImage is included with this distribution of Upload Files without COM.
Dim Width
Dim Height
Set Upload = New clsUpload
Set Image = New clsImage
Image.DataStream = Upload.Fields("File1").BinaryData
Select Case Image.ImageType
Case "GIF", "JPG", "PNG", "BMP"
FileName = Upload("File1").FileName
Folder = Server.MapPath("Uploads") & "\"
Upload("File1").SaveAs Folder & FileName
Case Else
Response.Write "File type not supported."
End Select
Set Image = Nothing
Set Upload = Nothing
I have published another script that supports this called Read Image Dimensions. It is included with this distribution as clsImage.asp.
With the Image Script, you can either read a file from the file system, or pass the binary data of the image to be read to it. You can then verify if the image size falls within your requirements and notify the user if it does not.
Dim Width
Dim Height
Set Upload = New clsUpload
Set Image = New clsImage
Image.DataStream = Upload.Fields("File1").BinaryData
If Image.Width > 640 Then
Response.Write "Image is too wide. It must be 640x480 or less."
ElseIf Image.Height > 480 Then
Response.Write "Image is too high. It must be 640x480 or less."
Else
FileName = Upload("File1").FileName
Folder = Server.MapPath("Uploads") & "\"
Upload("File1").SaveAs Folder & FileName
End If
Set Image = Nothing
Set Upload = Nothing
Each field that you upload has a Length property. You can check the length to make sure it is a specific size or less.
Dim Upload
Dim FileName
Dim Folder
Set Upload = New clsUpload
If Upload("File1").Length <= 1048576
FileName = Upload("File1").FileName
Folder = Server.MapPath("Uploads") & "\"
Upload("File1").SaveAs Folder & FileName
Else
Response.Write "File size must be 1 megabyte or less"
End If
Set Upload = Nothing
No. This is a restriction of the input element. However, you can have multiple input elements on one page. The visitor will have to populate each one with the location of the file on there computer.
The Field object has a property called BLOB. This can be saved directly into the database.
Dim Upload
Dim FileName
Dim Connection
Dim RecordSet
Dim ConnectionString
' One of these may work for you, depending on your systems configuration
ConnectionString = "DRIVER=Microsoft Access Driver (*.mdb);DBQ=" & Server.MapPath("Files.mdb")
ConnectionString = "PROVIDER=Microsoft.Jet.OLEDB.4.0;Data Source=" & Server.MapPath("Files.mdb")
Set Upload = New clsUpload
FileName = objUpload.Fields("File1").FileName
Set Connection = Server.CreateObject("ADODB.Connection")
Set RecordSet = Server.CreateObject("ADODB.Recordset")
Connection.Open ConnectionString
RecordSet.Open "Files", Connection, 3, 3
RecordSet.AddNew
RecordSet.Fields("FileName").Value = objUpload.Fields("File1").FileName
RecordSet.Fields("FileSize").Value = objUpload.Fields("File1").Length
RecordSet.Fields("ContentType").Value = objUpload.Fields("File1").ContentType
RecordSet.Fields("BinaryData").AppendChunk objUpload("File1").BLOB & ChrB(0)
RecordSet.Update
RecordSet.Close
Set RecordSet = Nothing
Set Connection = Nothing
Set Upload = Nothing
You can use the BinaryWrite method on the Response Object
Dim Connection
Dim RecordSet
Dim ConnectionString
Dim FileID
Dim Sql
' One of these may work for you, depending on your systems configuration
ConnectionString = "DRIVER=Microsoft Access Driver (*.mdb);DBQ=" & Server.MapPath("Files.mdb")
ConnectionString = "PROVIDER=Microsoft.Jet.OLEDB.4.0;Data Source=" & Server.MapPath("Files.mdb")
FileID = Request.QueryString("FileID")
Set Connection = Server.CreateObject("ADODB.Connection")
Set RecordSet = Server.CreateObject("ADODB.Recordset")
Connection.Open ConnectionString
Sql = "SELECT FileName, ContentType, BinaryData FROM Files WHERE FileID = " & FileID
RecordSet.Open Sql, Connection, 3, 3
If Not RecordSet.EOF Then
Response.AddHeader "content-disposition", "attachment; filename=" & RecordSet("FileName")
Response.AddHeader "content-length", RecordSet("BinaryData").ActualSize
Response.ContentType = RecordSet("ContentType")
Response.BinaryWrite RecordSet("BinaryData")
Else
Response.Write("File could not be found")
End If
RecordSet.Close
Connection.Close
Set RecordSet = Nothing
Set Connection = Nothing
You can work with the code that retrieves files from the database. Just reference the web page in your image tag.
<IMG src="Download.asp?FileID=327">
This is very similar to setting up permissions for folders. Rather then right-clicking on the folder, you can right-click on the file itself. The only permissions that you will need to verify are that they have access to read and modify the file.
Microsoft Access can store binary data in fields defined as OLE Object.
SQL Server has a few data types to store binary data. The quickest one to identify is the binary data type. This one is like a varchar data type (where you specify how long your data can be). The data type that I prefer to use is the image data type (similar to the text and ntext fields). The image data type holds a 16 byte pointer to your actual binary data and has a size limit of roughly 2 gigabytes. If you wish to restrict your data so that a file can not be stored that is greater then a certain size, then go with the binary data type, other wise - use image.
You can provide an extra text field and use this when saving the file. When the user sends the file over, you can then save the file with the suggested name.
<INPUT type="file" name="File1">
<INPUT type="text" name="FileName">
Dim Upload
Dim FileName
Dim Folder
Set Upload = New clsUpload
FileName = Upload("FileName").Value
Folder = Server.MapPath("Uploads") & "\"
Upload("File1").SaveAs Folder & FileName
Set Upload = Nothing
The upload class provides a method called UniqueName. You may pass the proposed file name, and the path of the folder that you wish to store it within. If a duplicate file name already exists within the folder, it will iterate through an index until it finds a name that does not yet exist.
If you upload a file called Photo.JPG 3 times, and use the UniqueName method each time, then you will have 3 files with the following names: Photo.JPG, Photo[1].JPG, Photo[2].JPG.
Dim Upload
Dim FileName
Dim Folder
Set Upload = New clsUpload
FileName = Upload("File1").FileName
Folder = Server.MapPath("Uploads") & "\"
FileName = Upload.UniqueName(Folder, FileName)
Upload("File1").SaveAs Folder & FileName
Set Upload = Nothing
The file is overwritten. You can use the UniqueName method to make sure that files are not over written by assigning them different names if a file with the same name already exists.
You can modify the number of bytes read at a time when the posted form data is read. This property can be found within clsUpload.asp. By default, the setting is 64 KB. Remember, as you decrease the value, the buffer is read and processed more often. If you are saving progress information, then a file is opened and written to each time as well. However, if you make the buffer too large, then your clients' progress window will not appear to get new information as often.
Do not setup your buffer to be under 100 bytes, or you may risk not being able to parse the data property for each field that had been posted to the web page.
Const BufferSize = &H10000
For your reference:
| Kilobytes | Bytes | Hex Value |
| 1 KB | 1024 | &H400 |
| 2 KB | 2048 | &H800 |
| 4 KB | 4096 | &H1000 |
| 8 KB | 8192 | &H2000 |
| 16 KB | 16384 | &H4000 |
| 32 KB | 32768 | &H8000 |
| 64 KB | 65536 | &H10000 |
| 128 KB | 131072 | &H20000 |
| 256 KB | 262144 | &H40000 |
The answer is not known. Some developers have mentioned that the script has worked perfectly with files over 100 MB for version 2. Others have mentioned that it begins to have problems when the files go up to that size, or even over 1 MB. It depends on your web servers' resources and connection speed to the visitor. It also depends if you are working with the latest version, as each one usually increases in performance.
As each version is released, it becomes more scaleable to allow larger file sizes. Hopefully, this version will allow more developers to approach the 100 MB test with success.
Version 2 helped increase the speed and take less memory to upload a file by introducing ADO Streams to handle conversion. Version 3 improves on the memory by constantly going to a central location to retrieve binary data rather then make small copies of it for each file uploaded. Also, data is not parsed until your code asks for it.
One of the downfalls of vbScript (and Visual Basic) is that in order to concatenate a string, you must copy it into memory twice. Take the following for example: (myString = myString & "xxxxx"). It is copied once to be assigned, and the other time is copied as the string being assigned.
This was a large problem with version 1 and slowed things down a lot when pulling binary content from the posted form data. The process was sped up by assigning a large chunk of the data to an ADODB.Stream object to convert the binary data for us. A lot of you who used version 1 were amazed when version 2 came out.
Two internal functions still remain that have a lot of concatenation (CStrU and CStrB). These two methods convert between Unicode and ASCII strings one character at a time. This is one of the last bottle necks left due to limitations of ASP. As long as your text values are small, the methods will not spend too much of your servers resources to convert the data. If you post a large resume in a TEXTAREA, then you may begin to see noticeable results.
Part of this question depends on your web server, another part depends on your web browser, and the last part depends on how well I've put my code together without knowing how to deal with Unicode. Your file names and content of the file may not transfer properly. I make no guarantees.
With that said, I don't have much to go on. From what I can understand, you may have to modify Meta tags and code pages on the server before you can properly test if this script supports double byte characters. I tried copying some double-byte characters from the internet and pasting it into one of my file names. After I had uploaded it with MSIE, the file was renamed. I tried this on both Windows XP and a Windows 2000 Server with the same exact results.
I tried copying the characters into the contents of a text file, and saved the file as Unicode. After uploading the file, I was still able to see the correct Unicode characters within it.
I tried the same tests with Mozilla 1.4. It replaced all the Unicode characters in the filename to underscore characters. Also, it only sent one byte of the files data across the line.
If you have had success or suggestions, please share your findings by sending me an email.
Just do a search for any portion of the signature in the ASP code. You may find it within an area where the class initializes. Please do not pass the code along to others after you had made changes.
If you are really interesting the full potential of what the script can do, take a look at the source code. It is commented very well so that even beginners can get a rough idea about what is happening.
I do not know. If you have ChilisoftASP or iASP, please try it out and tell me what the results are.
Sun ONE Active Server
Pages 4.0 - Chili!soft.ASP
Halcyon Software - iASP, Instant ASP
You can find this information if you search the internet for RFC 1867. (A.k.a. Request for Comments). As taken from the document itself, here is its explanation:
This memo defines an Experimental Protocol for the Internet community. This memo does not specify an Internet standard of any kind. Discussion and suggestions for improvement are requested. Distribution of this memo is unlimited.
This was back in 1995. RFC 1867 is no longer experimental. It is a widely accepted Internet standard.
This error has been caught in version 3 and gives details for working around it. This problem occurs because the Request.BinaryRead method may only be called once. Both the Request.Form and Upload class call this method. You have to make a choice. Do you want to use the Upload class to get your form data, or the standard Request.Form?
Most people run into this problem because they want to request other data posted on the form besides the file itself. The Upload class does support this. Use the following code to help you out.
Dim Upload
Dim Description
Set Upload = New clsUpload
Description = Upload("Description").Value
Set Upload = Nothing
See: What is the work-around to "Cannot call BinaryRead after using Request.Form"?
Your web server is not running with the latest version of Microsoft Windows Script. This script has been verified with vbScript version 5.5 and above. Microsoft Windows Script is available free of charge from the Microsoft web site.
Some web service providers do not allow you to create the Scripting.FileSystemObject object, or they restrict it to have limited functionality. If this is becoming a problem for you, then you can disable this feature. Look in clsUpload.asp for a Const variable named FileSystemObjectEnabled. Set this value to false and you should be ready to go.
Const FileSystemObjectEnabled = False
The file system object was used to add new features in this version that allow you to move, copy, delete, and rename files. It also aids in identifying a unique name for the file, and verifies the upload directory exists. It is not required in order to upload files or save them to the file system.
If an error isn't addressed within this FAQ, and the error message returned does not help you, then please contact me (lewis@moten.com).
Please contact me with the reason the code was changed and the source code that you updated it with. (lewis@moten.com)
For debugging purposes, I have created a DebugText method that you may use. I advise to use this with small text files, as the time to debug increases according to the amount of data that it has to parse. If you have a browser that is giving you problems, then you can send me the results returned from this method and I can look at the results and determine if I can come up with a solution to your problem.
Dim Upload
Set Upload = New clsUpload
Response.Write Upload.DebugText
Set Upload = Nothing
Open the clsUpload.asp file and look for the line that assigns a value to FileSystemObjectEnabled. You will need to set this value to false. If you disable the file system object, then you will not be able to display progress meters or interact with the file system through the upload class. However, you will still be able to save files to the file system and/or database.
Const FileSystemObjectEnabled = False