Pretty often it is necessary to send additional information along with files. Image Uploader comprehensively solves this task and supports uploading the following kinds of additional information:
This topic has three paragraphs, namely, uploading common data, uploading image-specific data and hash sums. Each of them describes approaches on how to send and receive relevant data, possibilities provided by these approaches, and code samples.
If you are unfamiliar with the Saving Uploaded Files in ASP.NET topic, please, read it first.
This section describes several approaches to uploading additional data. They are quite different but have two features in common, namely, independence of file types and upload of common data.
Using HTML forms is the most straight-forward way to upload data in web applications, thus, Image Uploader supports it. All you need is just to add the desired form to the same page where the Uploader control is hosted and set the Metadata.AdditionalFormName property to the name of created form. Then, when a user clicks Upload, the form data will be submitted along with user-selected files. On the server side, the fields of this form are accessible through the Package.PackageFields collection by their names.
An additional form can contain HTML input elements of different types, for example, you may use:
text
for an author namecheckbox
to determine whether to send a notification about the upload completionselect
to choose keywords related to the uploadThe following input types are not allowed: file
, image
, button
, and
reset
. Their values will not be sent at all.
The following configuration makes Image Uploader sending data from the addForm
form
along with uploaded files.
<form id="addForm" name="addForm"> <input ID="keywords" name="keywords" type="text" /> <input ID="addressee" name="addressee" type="text" /> </form> <form id="form1" runat="server"> <aur:Uploader ID="Uploader1" runat="server" OnAllFilesUploaded="AllFilesUploaded"> <Metadata AdditionalFormName="addForm" /> </aur:Uploader> </form>
Fields of the additional form are sent in each package, though in the following example we use the first package, which number is 0
,
because there is always at least one package in the upload session.
protected void AllFilesUploaded(object sender, Aurigma.ImageUploader.AllFilesUploadedEventArgs e) { string keywords = e.Packages[0].PackageFields["keywords"]; string addressee = e.Packages[0].PackageFields["addressee"]; }
Additional fields can be attached to the POST request using the metadata.addCustomField(String, String, Boolean) method. It accepts the following parameters:
You may add custom fields to the upload request only at runtime using the Image Uploader JavaScript API. Custom fields can use any format you like but should not conflict with the standard field names. See the POST Field Reference topic for a full list of standard Image Uploader fields.
On the server side, additional fields are accessible through the Package.PackageFields collection by their names.
The following example sends and receives an additional field named authorField
. Here the metadata.addCustomField(String, String, Boolean) method is called in the BeforeUpload event handler,
which fires when the upload is about to be started. For more information about events, please, see the using events section.
<script type="text/javascript"> function onBeforeUpload() { this.metadata().addCustomField('authorField', 'Author Name'); } </script> <form id="form1" runat="server"> <aur:Uploader ID="Uploader1" runat="server" OnAllFilesUploaded="AllFilesUploaded" > <ClientEvents> <aur:ClientEvent EventName="BeforeUpload" HandlerName="onBeforeUpload" /> </ClientEvents> </aur:Uploader> </form>
Additional fields are sent in each package, though in the following example we use the first package, which number is 0
,
because there is always at least one package in the upload session.
protected void AllFilesUploaded(object sender, Aurigma.ImageUploader.AllFilesUploadedEventArgs e) { string author = e.Packages[0].PackageFields["authorField"]; }
Each file uploaded via Image Uploader may have a description. There is no need to turn this possibility on, because it is enabled by default. So, let us examine how to get descriptions of uploaded files on a server using the FileUploaded event handler:
protected void FileUploaded(object sender, Aurigma.ImageUploader.FileUploadedEventArgs e) { string description = e.UploadedFile.Description; }
Tags give you an opportunity to send a hidden piece of data with each user-selected file. To attach such data use the file.tag property as it is demonstrated in the sample below. You may set tags only at runtime using the Image Uploader JavaScript API.
On the server side you may get a tag through the UploadedFile.Tag property.
The following example sends and receives tags for all selected files. Here the file.tag property is specified in the BeforeUpload event handler, which fires when the upload is about to be started. For more information about events, please, see the using events section.
<script type="text/javascript"> function onBeforeUpload() { var files = $au.uploader('Uploader1').files(); var count = files.count(); for (var i = 0; i < count; i++) files.get(i).tag('tag;file_' + i); } </script> <form id="form1" runat="server"> <aur:Uploader ID="Uploader1" runat="server" OnFileUploaded="FileUploaded"> <Converters> <aur:Converter Mode="*.*=SourceFile" /> </Converters> <ClientEvents> <aur:ClientEvent EventName="BeforeUpload" HandlerName="onBeforeUpload" /> </ClientEvents> </aur:Uploader> </form>
protected void FileUploaded(object sender, Aurigma.ImageUploader.FileUploadedEventArgs e) { string tag = e.UploadedFile.Tag; }
To pass data via the query string, just add necessary arguments to the upload script URL (UploadSettings.ActionUrl) and parse the request on a server as it is shown in the example below.
<aur:Uploader ID="Uploader1" runat="server"> <UploadSettings ActionUrl="Default.aspx?argument1=value1&argument2=value2" /> </aur:Uploader>
protected void Page_Load(object sender, EventArgs e) { if (System.String.Equals(Request.RequestType, "POST")) { string arg1 = Request["argument1"]; string arg2 = Request["argument2"]; } }
Image Uploader has some imaging capabilities, which are described in the Working with Images topic and Uploading Images section. Let us describe two of these features, which allow uploading additional data:
A rotation angle and crop bounds can be got through the Angle and CropBounds properties of the UploadedFile class respectively. Image Uploader measures rotation angle in degrees clockwise.
As soon as rotation and cropping of images are allowed by default, there is no need to configure Image Uploader to send them, so the following example shows only how to receive such data on a server.
protected void FileUploaded(object sender, Aurigma.ImageUploader.FileUploadedEventArgs e) { int angle = e.UploadedFile.Angle; Rectangle cropBounds = e.UploadedFile.CropBounds; }
To upload EXIF or IPTC values, you should specify the desired fields using the Metadata.Exif and Metadata.Iptc properties. Both these properties represent semicolon separated lists of field names which should be extracted and uploaded along with images. To specify a custom EXIF tag, for example, Windows image title, use the hexadecimal ID of the tag. Metadata.ValueSeparator sets a string separator for fields which can contain multiple string values, like IptcKeyword.
The following example sends two EXIF and two IPTC fields for each user-selected image:
<aur:Uploader ID="Uploader1" runat="server" OnFileUploaded="FileUploaded"> <Converters> <aur:Converter Mode="*.*=SourceFile" /> </Converters> <%--here 0x9c9b is the hexadecimal ID of the windows image title tag--%> <Metadata Iptc="IptcCopyrightNotice;IptcKeyword" ValueSeparator=";" Exif="ExifColorSpace;ExifDateTime;0x9c9b" /> </aur:Uploader>
The following example gets the EXIF and IPTC fields sent accordingly to the previous configuration and writes these values to the
Descriptions.xml
file. Here uploaded fields are contained in the Package.PackageFields
collection and can be retrieved by keys named in the following way: FieldName_xx
, where FieldName
is a name of an EXIF or IPTC field and xx
is the index of an uploaded file in this package. To get access to a custom EXIF tag use the ExifCustom_ID_xx
keys, where ID
is tag's hexadecimal ID.
protected void FileUploaded(object sender, Aurigma.ImageUploader.FileUploadedEventArgs e) { string galleryPath = Server.MapPath("Gallery"); System.Xml.XmlDocument descriptions = new System.Xml.XmlDocument(); if (System.IO.File.Exists(galleryPath + "Descriptions.xml")) descriptions.Load(galleryPath + "Descriptions.xml"); else descriptions.AppendChild(descriptions.CreateElement("files")); System.Xml.XmlElement file = descriptions.CreateElement("file"); file.SetAttribute("name", e.UploadedFile.SourceName); file.SetAttribute("CopyrightNotice", e.UploadedFile.Package.PackageFields["IptcCopyrightNotice_" + e.UploadedFile.Index]); file.SetAttribute("IptcKeyword", e.UploadedFile.Package.PackageFields["IptcKeyword_" + e.UploadedFile.Index]); file.SetAttribute("ExifColorSpace", e.UploadedFile.Package.PackageFields["ExifColorSpace_" + e.UploadedFile.Index]); file.SetAttribute("ExifDateTime", e.UploadedFile.Package.PackageFields["ExifDateTime_" + e.UploadedFile.Index]); //here 9C9B is the hexadecimal ID of the windows image title tag file.SetAttribute("Windows title", e.UploadedFile.Package.PackageFields["ExifCustom_9C9B_" + e.UploadedFile.Index]); descriptions.DocumentElement.AppendChild(file); descriptions.Save(galleryPath + "Descriptions.xml"); }
All date/time values have the following format: YYYY:MM:DD hh:mm:ss
. For example, May 11, 2006,
2:40PM would be represented as: 2006:05:11 14:40:00
.
Image Uploader Express does not support creating hash sums for files. See the Comparison of Image Uploader Editions topic for details.
File hash sum uniqely identifies a file; it is useful for accomplishing two main tasks: to check uploaded files integrity and to prevent duplicate file upload.
Image Uploader provides the Converter.Hash and Metadata.Hash properties to enable hash sums. The Converter.Hash property is used to enable hash values for checking upload integrity. If you use this property, Image Uploader will send hash value for a converted file along with the file. The Metadata.Hash property allows sending hash sums without sending files; it is useful to prevent duplicate files upload.
On the server side uploaded hash sums are accessible through the Package.PackageFields collection. Image Uploader encodes hash values using the Base64 algorithm; make sure that the server-side script uses the same algorithm. To send several hash values (calculated using different algorithms) specify the semicolon-separated list of algorithms as a property value.
Checking upload integrity works in the following way:
On the server side you can receive hash values using the following field name: FileNAlgorithmName_xx
, where AlgorithmName
is the name of hash algorithm (SHA, MD2, or MD5), N
is a zero-based index of the converter, and xx
is the index of an uploaded file in a package.
In the following two-part example Image Uploader sends an MD5 hash sum for each created thumbnail. The server-side script in the second part calculates MD5 hash value for received thumbnail, compares it with the uploaded sum, and saves the file if both hash sums are identical, which means that upload is successful.
<aur:Uploader ID="Uploader1" runat="server" OnFileUploaded="FileUploaded"> <Converters> <aur:Converter Mode="*.*=Thumbnail" Hash="MD5" /> </Converters> </aur:Uploader>
protected void FileUploaded(object sender, Aurigma.ImageUploader.FileUploadedEventArgs e) { Aurigma.ImageUploader.ConvertedFile sourceFile = e.UploadedFile.ConvertedFiles[0]; System.Security.Cryptography.MD5 md5 = new System.Security.Cryptography.MD5CryptoServiceProvider(); string actualMd5 = Convert.ToBase64String(md5.ComputeHash(sourceFile.FileStream), Base64FormattingOptions.InsertLineBreaks); string receivedMd5 = e.UploadedFile.Package.PackageFields["File0MD5_" + e.UploadedFile.Index]; if (string.Equals(actualMd5, receivedMd5)) { sourceFile.SaveAs(System.IO.Path.Combine(Server.MapPath("Gallery"), sourceFile.Name)); } }
Using the Metadata.Hash property you can detect duplicate files before they are uploaded to the server. The idea is to upload hash values for all user-selected files on the first step, then check whether these files have already been uploaded to the Web server in the past or not, and upload only the files not exisitng on the server. In general, the following actions should be implemented:
"*.*=None"
."*.*=SourceFile"
to enable sending original files.You can detect duplicates of original files only; not thumbnails.
On the server side you can receive hash values using the following field name: HashCodeAlgorithmName_xx
, where AlgorithmName
is one of the following algorithms: SHA, MD2, or MD5 and xx
is the index of an uploaded file in a package.
The following two-part example illustrates this approach. The first part configures Image Uploader, controls upload process, and deletes duplicate files from upload list (if any). The server-side script in the second part compares received hash values with the stored ones, sends back list of duplicate files (if any), and saves uploaded files and their hash values to file system. The example stores hash values in XML file solely for brevity; in a real-life application most likely you will use database instead.
<script type="text/javascript"> uploadCompletedMessage = ''; function onBeforeUpload() { if (this.converters().get(0).mode() == '*.*=None') { // Upload hashes only this.uploadPane().saveUploadList(1); this.metadata().addCustomField('hashcheck', '1'); uploadCompletedMessage = this.messages().uploadCompleted(); this.messages().uploadCompleted(''); } else { this.uploadPane().resetUploadList(1); this.metadata().removeCustomField('hashcheck'); this.messages().uploadCompleted(uploadCompletedMessage); } } function onAfterUpload(data) { if (this.converters().get(0).mode() == '*.*=SourceFile') { this.converters([{ mode: '*.*=None'}]); return; } if (this.converters().get(0).mode() == '*.*=None') { this.converters([{ mode: '*.*=SourceFile'}]); this.uploadPane().loadUploadList(1); if (data) { var indexes = data.split(';'); // Remove duplicates from upload list for (var i = indexes.length - 1; i >= 0; i--) this.files().remove(parseInt(indexes[i], 10)); } if (this.files().count() > 0) { // Upload files var u = this; setTimeout(function() { u.upload(); }, 100); } else this.converters([{ mode: '*.*=None'}]); } } </script> <form id="form1" runat="server"> <aur:Uploader ID="Uploader1" runat="server" OnAllFilesUploaded="AllFilesUploaded"> <ClientEvents> <aur:ClientEvent EventName="AfterUpload" HandlerName="onAfterUpload" /> <aur:ClientEvent EventName="BeforeUpload" HandlerName="onBeforeUpload" /> </ClientEvents> <Converters> <aur:Converter Mode="*.*=None" /> </Converters> <Metadata Hash="MD5" /> </aur:Uploader> </form>
private List<string> hashes = new List<string>(); private List<string> existedFiles = new List<string>(); protected void AllFilesUploaded(object sender, AllFilesUploadedEventArgs e) { string galleryPath = Server.MapPath("Gallery"); System.Xml.XmlDocument descriptions = new System.Xml.XmlDocument(); if (System.IO.File.Exists(Path.Combine(galleryPath, "Descriptions.xml"))) descriptions.Load(Path.Combine(galleryPath, "Descriptions.xml")); else descriptions.AppendChild(descriptions.CreateElement("files")); if (!string.IsNullOrEmpty(Request["hashcheck"])) { //read all hash sums from Descriptions.xml hashes.Clear(); System.Xml.XmlNodeList hashSums = descriptions.SelectNodes("/files/file[@md5]/@md5"); for (int i = 0; i < hashSums.Count; i++) { System.Xml.XmlNode hashSum = hashSums[i]; hashes.Add(hashSum.Value.ToString()); } //check for equal hash sums foreach (UploadedFile uploadedFile in e.UploadedFiles) { string hash = uploadedFile.Package.PackageFields["HashCodeMD5_" + uploadedFile.Index]; if (hashes.Contains(hash)) existedFiles.Add(uploadedFile.Index.ToString()); } Response.ClearContent(); Response.Write(string.Join(";", existedFiles.ToArray())); Response.End(); } foreach (UploadedFile uploadedFile in e.UploadedFiles) { // Get source file ConvertedFile sourceFile = uploadedFile.ConvertedFiles[0]; if (sourceFile != null) { // Save file to the disk sourceFile.SaveAs(Path.Combine(galleryPath, uploadedFile.SourceName)); //Add file hash sums to the Descriptions.xml file System.Xml.XmlElement file = descriptions.CreateElement("file"); file.SetAttribute("name", uploadedFile.SourceName); file.SetAttribute("md5", uploadedFile.Package.PackageFields["HashCodeMD5_" + uploadedFile.Index]); descriptions.DocumentElement.AppendChild(file); descriptions.Save(Path.Combine(galleryPath, "Descriptions.xml")); } } }