LinkButtons
do not start the progress display in Internet Explorer
for the MacBrettle.Web.NeatUpload.dll
with the copy from
NeatUpload-1.2.x and copy
Progress.js
from NeatUpload-1.2.x/NeatUpload/
to the NeatUpload/
subfolder in your web application.
ProgressBar
s must now
be placed somewhere inside a <form>
element. In almost all cases that will already be the case so
this should not be a major issue. Brettle.Web.NeatUpload.dll
with the copy from
NeatUpload-1.1.x and add
Progress.js
and Error413.aspx
from NeatUpload-1.1.x/NeatUpload/
to the NeatUpload/
subfolder in your web application.
If you have not customized your Progress.aspx
page, you
should replace it with NeatUpload-1.1.x/NeatUpload/Progress.aspx
.
If you have customized it, consider changing it to use
the new <Upload:DetailsSpan>
and <Upload:DetailsDiv>
elements to take advantage of AJAX
refreshless updates
. See Customizing
Progress.aspx for details.<neatUpload>
configuration section which can be
placed either in the <brettle.web>
or the <system.web>
section
group,
either of which can be put inside of the <location>
element to apply
configuration settings to specific
pages.UploadStorageProvider
s.<Upload:DetailsSpan>
and <Upload:DetailsDiv>
elements.Url
property to ProgressBar
control to specify location of progress display page (defaults to NeatUpload/Progress.aspx
).Button.Click
handlers).Triggers
property
to ProgressBar
which can be used instead of
the ProgressBar.AddTrigger()
method.InputFile.FileName
property to always return just the
client-side file name (e.g. foo.txt
), even if
the browser sends the
full path (e.g. C:\dir\foo.txt
).
Since, Internet Explorer is the
only popular browser to send the full path, and the full path is
difficult to handle reliably on non-Windows servers which don't use
backslash ("\") as a path separator, returning the full path was likely
to result in code which is browser-specific and not portable.Progress.js
to reduce bandwidth required to download Progress.aspx
repeatedly.Demo.aspx.cs
from Brettle.Web.NeatUpload.dll
.
It is now compiled on the fly when Demo.aspx
is requested.NeatUpload allows ASP.NET developers to stream uploaded files to disk and allows users to monitor upload progress. It is open source and works under Mono's XSP / mod_mono as well as Microsoft's ASP.NET implementation.
NeatUpload contains four custom controls (InputFile
,
ProgressBar
, DetailsSpan
,
and DetailsDiv
), an HttpModule
(UploadHttpModule
), and a Page subclass (ProgressPage
).
This section briefly describes what they each do and how they are
related. The remainder of this manual describes how to install and use
NeatUpload.
InputFile
is a custom control that renders like HtmlInputFile
,
but provides properties to access the
uploaded file's client-specified name, content, and MIME type, and a
method to
move the file to a permanent location.
ProgressBar
is a custom control that is responsible for providing a site for the
progress to be displayed. It provides an attribute to control whether
to display the progress inline or in a popup. It also provides ways to
control which buttons cause the progress
display to start refreshing. If the progress is displayed inline, the ProgressBar
control renders as an IFRAME
. Otherwise, it
renders as a DIV
containing
a "Check Upload Progress" link (to display the progress in a new
window) along with some JavaScript which removes the DIV
when the page
loads. This provides a fallback when JavaScript is not available. Note
that ProgressBar
doesn't actually display the
progress bar itself. It merely provides a site (an IFRAME
or popup)
which loads a page derived from ProgressPage
(by default Progress.aspx
). The ProgressPage
subclass displays the progress bar.
UploadHttpModule
is an HttpModule
that intercepts HTTP
requests, streams InputFile
uploads to
temporary files, and restricts the size of the remainder of the
request. By default, UploadHttpModule
intercepts every
request. As a result, a bug in NeatUpload could affect pages
that don't even contain InputFile
controls. For this reason, you can configure NeatUpload to only use UploadHttpModule
for certain pages (or even none) while continuing to use InputFile
and ProgressBar
. When UploadHttpModule
is not used for a request, NeatUpload gets the uploaded file from the
ASP.NET standard HttpRequest.Files
property instead of intercepting the request. That means that InputFile
will function like an HtmlInputFile
control,
but no progress bar will be displayed. This greatly reduces the risk of
adopting NeatUpload.
ProgressPage
is a subclass of System.Web.UI.Page
that is used as the base class for pages displayed in the site provided
by the ProgressBar
control (i.e. in the IFRAME
or popup). Progress.aspx
is the default ProgressPage
. ProgressPage
retrieves
the details of the upload process from the UploadHttpModule
.
and makes them available for subclasses to use in data-binding
expressions. ProgressPage
subclasses (e.g. Progress.aspx
) place such
data-binding expressions within DetailsSpan
and DetailsDiv
controls so that
NeatUpload can use AJAX techniques to update the values without
requiring a browser a refresh.
To use NeatUpload you will need:
Installing NeatUpload is pretty straightforward. Just copy
the necessary assemblies and subdirectories into your application and
make some
modifications to your Web.config
.
Specifically:
NeatUpload-1.1.x/
. NeatUpload-1.1.x/
is a web application. Then point your browser at the Demo.aspx
on that
website. To use NeatUpload in your own web application, continue with
the remaining installation steps. NeatUpload-1.1.x/bin/Brettle.Web.NeatUpload.dll
,
click Open, and then click
OK. A reference to Brettle.Web.NeatUpload.dll
will
automatically be added to your project the first time you use the
designer to add one of the NeatUpload controls to a form.Brettle.Web.NeatUpload.dll
in the NeatUpload-1.
1.x
/bin
directory. NeatUpload/
subdirectory in
your web application by copying
NeatUpload-1.
1.x
/NeatUpload/
and its contents. Don't copy the whole NeatUpload-1.
1.x
/
directory, just the NeatUpload-1.
1.x
/NeatUpload/
subdirectory. That subdirectory contains Progress.aspx
and associated files. Web.config
under configuration/system.web/httpModules
:
<add name="UploadHttpModule" type="Brettle.Web.NeatUpload.UploadHttpModule, Brettle.Web.NeatUpload" />
Web.config
under configuration/system.web
:
<httpRuntime maxRequestLength="size_in_kbytes" />At the moment, both .NET and Mono seem to ignore that setting when NeatUpload is being used, but there is no official documentation specifying exactly when that limit is enforced, so a future version of .NET or Mono might enforce the limit even when NeatUpload is being used. Setting the
<httpRuntime>
element's maxRequestLength
attribute simply provides insurance against such future changes.
Note
that since that attribute is currently ignored when NeatUpload is used,
you can't use it to actually restrict the size of uploads. To
do that,
see Limiting
the Size of Upload Requests below.Demo.aspx
and Demo.aspx.cs
from NeatUpload-1.
1.x
/
into your application. Point your browser at Demo.aspx
and verify that the demo functions properly. To use the Visual Studio web form designer to add NeatUpload to a web form, follow these steps:
InputFile
, ProgressBar
,
and Button
.ProgressBar
's
Inline
property to true
.
ProgressBar
fallback behavior, drag whatever control(s) you want displayed as a
fallback onto the ProgressBar
control.
For example, to just change the fallback text, you could drag
a Label
control onto the ProgressBar and edit
its contents.inputFileId.FileName
,
inputFileId.ContentType
,
and inputFileId.FileContent
,
respectively.
If you want to
keep the uploaded file, you must use the inputFileId.MoveTo()
method
to
move the uploaded file to a permanent location. If you do
not,
NeatUpload will automatically remove the uploaded file at the end of
the request to ensure that unwanted files are not left on
the filesystem. The following code will put the uploaded file
in the
application's root directory (assuming
sufficient permissions):
using Brettle.Web.NeatUpload;
...
using System.IO;
...
public class YourPage : System.Web.UI.Page
{
private void Page_Load(object sender, EventArgs e)
{
...
submitButtonId.Click += new System.EventHandler(this.Button_Clicked);
...
}
...
private void Button_Clicked(object sender, EventArgs e)
{
...
if (IsValid && inputFileId.HasFile)
{
...
inputFileId.MoveTo(Path.Combine(Request.PhysicalApplicationPath, inputFileId.FileName),
MoveToOptions.Overwrite);
To use NeatUpload on a web form without using the Visual Studio designer, follow these steps:
<%@ Register TagPrefix="Upload" Namespace="Brettle.Web.NeatUpload" Assembly="Brettle.Web.NeatUpload" %>
InputFile
control to your
aspx page wherever you
want the user to choose a file, using something like this:
<Upload:InputFile id="inputFileId" runat="server" />Feel free to add any attributes that you would normally add to an
HtmlInputFile
tag. ProgressBar
control to
your aspx page wherever you
want to display an inline progress bar or, in the case of a popup
progress bar, add it wherever you want the fallback link to be
displayed. Use something like this:
<Upload:ProgressBar id="progressBarId" runat="server" inline="true|false" />The inline attribute defaults to false.
<asp:Button id="submitButtonId" runat="server" Text="Submit" />
InputFile
,
ProgressBar
, and Button
control using code like this:
using Brettle.Web.NeatUpload;
...
using System.Web.UI.WebControls;
...
public class YourPage : System.Web.UI.Page
{
...
protected InputFile inputFileId;
protected ProgressBar progressBarId;
protected Button submitButtonId;
inputFileId.FileName
,
inputFileId.ContentType
,
and inputFileId.FileContent
,
respectively.
If you want to
keep the uploaded file, you must use the inputFileId.MoveTo()
method
to
move the uploaded file to a permanent location. If you do
not,
NeatUpload will automatically remove the uploaded file at the end of
the request to ensure that unwanted files do not fill up
the filesystem. The following code will put the uploaded file
in the
application's root directory (assuming
sufficient permissions):
using Brettle.Web.NeatUpload;
...
using System.IO;
...
public class YourPage : System.Web.UI.Page
{
private void Page_Load(object sender, EventArgs e)
{
...
submitButtonId.Click += new System.EventHandler(this.Button_Clicked);
...
}
...
private void Button_Clicked(object sender, EventArgs e)
{
...
if (IsValid && inputFileId.HasFile)
{
...
inputFileId.MoveTo(Path.Combine(Request.PhysicalApplicationPath, inputFileId.FileName),
MoveToOptions.Overwrite);
InputFile
and a ProgressBar
, the progress display is
started (either inline or in a popup). If all InputFiles
are empty, the progress display is not started. That ensures
that
the user is not distracted by a transient and meaningless progress
display.InputFile
controls
so that no files will be uploaded and the progress display will not
start. (On some downlevel browsers, notably Internet Explorer
for
the Mac, NeatUpload can't clear the controls so it displays a dialog
asking the user to clear them manually and try again. The
text
for that dialog can be customized via the ClearFileNamesAlert
resource in Strings.resx
.)ProgressBar
, each
ProgressBar
which specifies trigger controls will only start when
the form submission is initiated by one of those controls.ProgressBar
's
Triggers
property (multiple IDs
should be space-separated), or you can call the ProgressBar's AddTrigger(Control)
method. Note: if you call the AddTrigger(Control)
method, you need to call it each time the page is loaded (even when Page.IsPostBack
is true
) because the list of trigger controls
is not maintained in the page's ViewState
.InputFile
control.
You should read the earlier sections before reading this one.http://www.tempuri.org/path/to/MyUploadPage.aspx?NeatUpload_PostBackID=afa02a6999e54541bb6873151d1dfbfc
InputFile
controls and will stream them all to storage. To ensure that
you
can access the uploaded files from your page, make sure that the name
of the form field(s) used by the client application match the control
IDs of your InputFile
control(s).
So, if your client application uses a field name of
"FILE001", you should use put the following in your page:...
<Upload:InputFile id="FILE001" runat="server" />
...
<neatUpload>
Configuration SectionWeb.config
:<configuration>
<configSections>
<sectionGroup name="system.web">
<remove name="neatUpload"
/>
<section name="neatUpload"
type="Brettle.Web.NeatUpload.ConfigSectionHandler,
Brettle.Web.NeatUpload" allowLocation="true" />
</sectionGroup>
</configSections>
...
sectionGroup
"name" attribute can
be either "system.web" or "brettle.web". Although using
"brettle.web" is better for preventing name collisions, it makes it
impossible to do location filtering under Mono (at least as of
1.1.9.2). Even under .NET, you will have to do more typing to
configure location filtering if you use "brettle.web" than if you use
"system.web". The remainder of this document assumes you are
using "system.web".Web.config
's
<system.web> element(s), like this:
<configuration>
...
<system.web>
...
<neatUpload
useHttpModule="true or false, defaults to true"
maxNormalRequestLength="
up to 2097151
in KBytes, defaults to 4096"
maxRequestLength="
up to 2097151
in KBytes
, defaults to
2097151
"
postBackIDQueryParam="parameter name, defaults to
NeatUpload_PostBackID"
defaultProvider="friendly name, defaults to a
FilesystemUploadStorageProvider using the system temp dir">
<providers>
<add name="friendly
name"
type="type derived from
Brettle.Web.NeatUpload.UploadStorageProvider"
provider-specific-attributes
... />
<remove name="friendly name of provider"
/>
<clear />
</providers>
</neatUpload>
...
</system.web>
...
</configuration>
<providers>
element is
optional. You only need to use it if you are using an UploadStorageProvider
other than the default FilesystemUploadStorageProvider
,
or you need to change the temporary directory used by the FilesystemUploadStorageProvider
.
In those cases, you should include an <add>
element for
each provider or provider configuration you will be using and set the
<neatUpload> element's defaultProvider
attribute to the name you chose for the provider you want to use by
default. For specific pages or directories, you can override
that
default or add/remove/clear available providers using child
configuration sections. Child configuration sections are
<neatUpload> elements within <location>
elements or in
Web.config files in subdirectories. By default, NeatUpload puts uploaded files in temporary files
in the system's
temporary directory. You can specify a different directory
using the tempDirectory
attribute of the FilesystemUploadStorageProvider
,
like this:
<neatUpload ...If you specify a relative path, it will be relative to your application root directory.defaultProvider="FilesystemUploadStorageProvider" ...
><providers>
...
<add name="FilesystemUploadStorageProvider
"
type="Brettle.Web.NeatUpload.FilesystemUploadStorageProvider
"
tempDirectory="path, defaults to path returned by System.IO.Path.GetTempPath()"
... />
...</providers>
</neatUpload>
UploadHttpModule
is added via the <httpModules>
section of your Web.config
, it intercepts and
filters all requests
sent to your application. For upload requests, it streams the
uploaded files to disk and makes them available to your code via InputFile
.
It also limits the size of the non-upload part of the request
and the total size of
non-upload requests
because that request data is stored in server memory. If it
didn't do that an attacker could mount a Denial of Service attack by
sending a request that contains up to maxRequestLength
kilobytes of non-file data.UploadHttpModule
by
settting the <neatUpload>
element's useHttpModule
attribute to
false. If you want to use the UploadHttpModule
for only some
requests, you can use ASP.NET's <location>
element to
specify which pages it should be used for. You can also use
the <location>
element to
control which UploadStorageProvider
is used
for specific pages. For example,
consider the following configuration:<configuration>
...
<system.web>
...
<httpRuntime maxRequestLength="100" />
...
<httpModules>
<add name="UploadHttpModule"
type="Brettle.Web.NeatUpload.UploadHttpModule, Brettle.Web.NeatUpload"
/>
...
</httpModules>
...
<neatUpload useHttpModule="false" maxNormalRequestLength="100"
maxRequestLength="2097151">
<providers>
<add name="special"
type="Brettle.Web.NeatUpload.FilesystemUploadStorageProvider"
tempDirectory="SpecialTempDirectory"
/>
</providers>
</neatUpload>
...
</system.web>
...
<location path="Demo.aspx">
<system.web>
<neatUpload useHttpModule="true"
/>
<httpRuntime
maxRequestLength="2097151" executionTimeout="3600" />
</system.web>
</location>
<location
path="Special.aspx">
<system.web>
<neatUpload useHttpModule="true"
defaultProvider="special" />
<httpRuntime
maxRequestLength="2097151" executionTimeout="3600" />
</system.web>
</location>
...
</configuration>
That configuration will cause the UploadHttpModule
to only be used for requests to Demo.aspx
and Special.aspx
.
In addition, ASP.NET's maxRequestLength
is set to only 100 KBytes for all requests other than those two pages.
For those two pages
, the maxRequestLength
is increased to 2 GBytes and the executionTimeout
is increased to 1 hour. Also, requests for Special.aspx
use the "special" provider which is configured to use the
"SpecialTempDirectory
", while requests for Demo.aspx
use the default provider which uses the system temporary directory.UploadHttpModule
in the <httpModules>
section so that it will be available to your application. Location filtering
just gives you finer control over which requests the UploadHttpModule
actually touches.By default, NeatUpload does not directly limit the size of
uploads.
To limit upload size, use the <neatUpload>
element's maxRequestLength
attribute:
<neatUpload ... maxRequestLength="sizeInKBytes" ... />
If a user attempts to upload a file larger than the specified size, NeatUpload will update the progress bar to indicate that the upload was rejected because it was too large. If the progress bar is in a pop-up window, it will leave the window open to ensure that the user sees why the upload was rejected. Next it will attempt to stop the upload by asking the browser to run JavaScript that simulates clicking the browser's Stop button. If JavaScript is enabled, most modern browsers (including recent versions of IE, Firefox, and Opera) will be able to stop the upload and the user will simply see the original form along with the progress bar displaying the reason the upload was rejected. The remainder of this section only affects the user when the browser is not able to stop the upload.
After asking the browser to stop the upload using JavaScript,
NeatUpload reads the remainder of the request and then throws
an UploadTooLargeException
. The UploadTooLargeException
class
is a subclass of UploadException
(which is a
subclass of HttpException
) with an HTTP
status code of 413. If the browser hasn't stopped the upload
for some reason, the exception will cause the user to see a
generic error
page by default. If you want something
more
user-friendly, you can either handle the error by adding a handler for
the HttpApplication.Error
event, or you can
use a custom
error page. NeatUpload comes with an example custom error
page. To
use it, add the following to your Web.config
under
configuration/system.web
:
<customErrors mode="On">
<error statusCode="413" redirect="~/NeatUpload/Error413.aspx" />
</customErrors>
Note:
most browsers, including IE and Firefox, won't display any new content
until
the entire upload has been sent to the server. If that takes
longer
than the amount of time specified via the httpRuntime
element's executionTimeout
attribute, the server may choose to close the connection and the user
will see a different error. IE will display a generic error
page
saying "Page cannot be displayed". Firefox will display a
dialog
saying "Document contains no data". The default executionTimeout
is 360 seconds.
By default, NeatUpload
restricts the size of
non-upload requests and the non-upload portion of upload requests to 4
MBytes. To specify a different maximum
size, use the <neatUpload>
element's maxNormalRequestLength
attribute:
<neatUpload ... maxNormalRequestLength="sizeInKBytes" ... />The behavior when this value is exceeded is similar to the behavior when the
maxRequestLength
is exceeded. See the preceding section for details.
The
only difference is that for non-upload requests that exceed the maxNormalRequestLength
limit, NeatUpload does not attempt to use JavaScript to interrupt the
browser and the exception that is thrown is an HttpException(413,
"Request Entity Too Large")
instead of an UploadTooLargeException()
.<neatUpload>
element's postBackIDQueryParam
attribute:<neatUpload ... postBackIDQueryParam="parameterName" ... />The default value is "NeatUpload_PostBackID".
StorageConfig
property of
each InputFile
control to change the way the UploadStorageProvider
handles the file uploaded to that control. The interpretation
of InputFile.StorageConfig
depends on
which UploadStorageProvider
is used.
The default FilesystemUploadStorageProvider
only allows the temporary directory to be set via the StorageConfig
property. To programmatically set the temporary directory
place a line like this in Page_Load()
:inputFileId.StorageConfig["tempDirectory"]
= "path_to_temp_directory";
StorageConfig
property, it
is critical to understand how the StorageConfig
is communicated to the UploadStorageProvider to avoid some possible
pitfalls. When the InputFile
control is rendered, NeatUpload encrypts the StorageConfig
with an encryption key, signs it with a validation key and then renders
the ciphertext and signature in a hidden form field. When
NeatUpload receives the upload request, it retrieves the form field
value, verifies the signature, and decrypts the ciphertext to get the StorageConfig
.
By default, NeatUpload automatically generates random
encryption
and validation keys when they are first needed during the application
lifetime. However, you can set them explicitly by setting the
encryptionKey
and validationKey
attributes of the <neatUpload>
element to random 32 and 40 digit hexadecimal strings, respectively.StorageConfig
prevents an attacker from examining and modifying it.
However, it
does not prevent replay attacks and it will cause uploads to be
rejected if the server uses different keys for validation/decryption
than it did for signing/encryption. Follow the
following rules to avoid these problems:StorageConfig
to
configure an InputFile
control if you don't
mind an attacker associating that StorageConfig
with other InputFile
controls while
the same validation key is being used. This is not typically
a problem for StorageConfig["tempDirectory"]
because it is mostly used to specify a temporary directory on the same
drive as the final location of the file to improve performance.
If an attacker used a StorageConfig
from a
different control, they would typically just get poor performance.
Note, however, that if you decide to stop using a temporary
directory, you should either change the validation key or use some
other mechanism (e.g. change the permissions on the directory) to
ensure that an upload that uses an old StorageConfig
can not write to the old directory.StorageConfig
to
configure an InputFile
control if you don't mind having uploads rejected while transitioning
from old to new keys. NeatUpload will reject any uploads from
forms rendered with the old key. The default rejection
message is
the value of the InvalidStorageConfigMessageFormat
resource in Strings.resx
. It
defaults to "Please refresh the page and try again."StorageConfig
in an
environment (e.g. a
web farm) where the server rendering the form could be different from
the server that receives the upload request, unless you manually
configure all servers to use the same keys.
SqlServerInputFile is a NeatUpload extension generously contributed by
Joakim Wennergren (jokedst at gmail dot com). It streams uploaded files
to and from an SQL Server database and consists of the SqlServerInputFile
web control and a custom UploadStorageProvider
called SqlServerUploadStorageProvider
.
SqlServerInputFile
:SqlServerInputFile
control to your
toolbox. To do that,
right-click
on the Toolbox, click Add/Remove Items, Browse to
NeatUpload-1.2.x/Extensions/SqlServerInputFile/SqlServerUploader/bin/Hitone.Web.SqlServerUploader.dll
,
click Open, and then click
OK. A reference to Hitone.Web.SqlServerUploader.dll
will
automatically be added to your project the first time you use the
designer to a SqlServerInputFile to a form.Hitone.Web.SqlServerUploader.dll
in the NeatUpload-1.
2.x/Extensions/SqlServerInputFile/SqlServerUploader/bin/
directory.SqlServerUploadStorageProvider
,
add the following to your Web.config
:
<configuration>
<configSections>
<sectionGroup name="system.web">
<remove name="neatUpload"
/>
<section name="neatUpload"
type="Brettle.Web.NeatUpload.ConfigSectionHandler,
Brettle.Web.NeatUpload" allowLocation="true" />
</sectionGroup>
</configSections>
...
<system.web>
...
<neatUpload ...
defaultProvider="SqlServerUploadStorageProvider">
<providers>
<add name="
SqlServerUploadStorageProvider
"
type="Hitone.Web.SqlServerUploader.SqlServerUploadStorageProvider, Hitone.Web.SqlServerUploader"
connectionString="connection string (required unless using connectionName to identify named connection in ASP.NET 2.0)"
options identifying stored procs or table/column names (required)
hashAlgorithm="optional name of hash algorithm supported by .NET (e.g. MD5, SHA1, etc), defaults to no hash"/>
</providers>
</neatUpload>
...
</system.web>
...
</configuration>
The SqlServerUploadStorageProvider
can either generate SQL itself
or use stored procedures on the database server for all tasks.
This is controlled by the attributes you specify when you add the provider to your Web.config
.
When the provider needs to do something, it first checks to see if you have specified a stored procedure to do it
using one of the following attributes:
createProcedure
- Procedure that takes @FileName and @MIMEType (both varchars) input
parameters, and sets @Identity (Numeric) and @Pointer (Binary(16)) output parameters. The
@Identity and @Pointer values that are returned will be passed to the writeProcedure
as the file
is received. The @Identity value will also be passed to the deleteProcedure
,
cleanupProcedure
, renameProcedure
, and storeHashProcedure
.writeProcedure
- Procedure that takes @Identity
(Numeric), @Pointer (Binary(16)), @Offset (Int), @Delete (Int), and @Bytes (varbinary) input parameters.
It should replace the @Delete bytes at position @Offset in the file identified by @Identity and
@Pointer with the bytes in @Bytes.cleanupProcedure
- The provider calls this procedure with @Identity (Numeric) when an upload is
no longer in progress and it will not call writeProcedure
anymore. Note that this procedure is
called even if the upload did not complete successfully for some reason.storeHashProcedure
- If the hashAlgorithm
attribute is set, the provider calls
this procedure with @Identity (Numeric) and @Hash (varchar). @Hash is the hash value computed for the file
identified by @Identity.renameProcedure
- The provider calls this procedure with @Identity (Numeric) and @FileName
(varchar) to indicate that the application has requested that the name of the file identified by @Identity be
changed to @FileName. This happens when you call inputFileId.MoveTo()
.deleteProcedure
- Procedure that takes @Identity (Numeric) input parameter and deletes the
specified file. This is called at the end of the request if your application called neither
inputFileId.MoveTo() nor inputFileId.Verify()
openProcedure
- This procedure is called if you use
inputFileId.FileContent
to access the uploaded file as a stream. It takes
an @Identity (Numeric) input parameter, and sets @Pointer (Binary(16)), @FileName (varchar), and @MIMEType
(varchar) output parameters. The @Pointer value that is returned will be passed to the
readProcedure
as content is read from the stream.readProcedure
- This procedure is called if you use
inputFileId.FileContent
to access the uploaded file as a stream. It takes @Identity
(Numeric), @Pointer (Binary(16)), @Offset (Int), and @Size (Int) input parameters and returns the @Size bytes
starting at position @Offset in the file identified by @Identity and @Pointer.If you don't provide a stored procedure for what the provider needs to do, it will generate the SQL to do it. When generating SQL queries, the provider uses the values you specify with the following attributes:
tableName
- Name of the table to contain the files. "FileTable" in the example below.dataColumnName
- Name of an image/varbinary column for the contents of the uploaded file.
"DataField" in the example below.partialFlagColumnName
- Name of a tinyint column that the provider will set to 1 while the upload
is being received and set to 0 when the upload is complete. "Partial" in the example below.fileNameColumnName
- Name of a nvarchar column for the name of the uploaded file. While the
upload is being received, the provider will set the column to the name (excluding path provided by the browser).
If you call inputFileId.MoveTo(), the new name will be stored in this column. "FileName" in the
example below.mimeTypeColumnName
- Name of a nvarchar column for the browser-provided MIME content type of the
file "MimeType" in the example below.hashColumnName
- Name of a nvarchar column for the hash of the file contents (as a hex string).
"FileHash" in the example below.
To generate SQL queries, the provider only requires tableName
and dataColumnName
. The
remaining attributes are optional.
Note: The
generated SQL will not work with SQL Server 2000 because it requires the "$IDENTITY" object
to reference the IDENTITY column of the table. An identityColumnName
attribute might be added in a
future release. Until then, SQL Server 2000 users will need to use stored procedures to workaround around this
limitation.
SqlServerInputFile
to your forms:SqlServerInputFile
control from your Toolbox and drop it on the form.SqlServerInputFile
control to
your
aspx page wherever you
want the user to choose a file, using something like this:
<SqlUpload:SqlServerInputFile id="inputFileId" runat="server" />
InputFile
or HtmlInputFile
control.InputFile
object. In addition, SqlServerInputFile
offers a Verify()
method that tells the SqlServerUploadStorageProvider
not to delete the
uploaded file at the end of the request. Verify()
is equivalent to calling
inputFileId.MoveTo(inputFileId.FileName, MoveToOptions.Overwrite)
but doesn't cause the extra
database update.
SqlServerInputFile
also adds Hash
, HashSize
, and HashAlgorithm
properties which return information about the cryptographic hash (if any) computed for the uploaded file.
There is an example web application in Extensions/SqlServerInputFile/UploaderTest
. To use it:
Extensions/SqlServerInputFile/UploaderTest
it's own web application.Web.config
to point to your database.CREATE TABLE [FileTable] ( [Id] [int] IDENTITY (1, 1) PRIMARY KEY NOT NULL , [FileName] [nvarchar] (50) NULL, [DataField] [image] NOT NULL , [Partial] [tinyint] NOT NULL , [MimeType] [nvarchar] (50) NULL , [Created] [datetime] NOT NULL CONSTRAINT [DF_FileTable_Created] DEFAULT (getdate()), [FileHash] [nvarchar] (50) NULL , ) GO Alter Procedure CreateBlob @Identity Numeric Output, @Pointer Binary(16) Output, @FileName VarChar(250) = null, @MIMEType VarChar(250) = null As Begin Set NoCount ON; Insert Into FileTable (Datafield,FileName,MimeType,PartiallyUploaded) Values ('',@FileName,@MimeType,1) Select @Identity = SCOPE_IDENTITY() Select @Pointer = TEXTPTR(DataField) From FileTable Where $IDENTITY = @Identity End Go Alter Procedure OpenBlob @Identity Numeric, @Pointer VarBinary(max) Output, @Size Int Output, @FileName VarChar(250) Output, @MIMEType VarChar(250) Output As Begin Set NoCount On Select @Pointer = TEXTPTR(DataField), @Size = DATALENGTH(DataField), @FileName = [FileName], @MIMEType = MIMEType From FileTable Where $IDENTITY = @Identity End Go Alter Procedure ReadBlob @Identity Numeric, --ignored in this implementation, here for reference @Pointer Binary(16), @Offset Int, @Size Int As Begin Set NoCount On ReadText FileTable.DataField @Pointer @Offset @Size End Go Alter Procedure WriteBlob @Identity Numeric, --ignored in this implementation, here for reference @Pointer Binary(16), @Bytes VarBinary(max), @Offset Int, @Delete Int As Begin Set NoCount On UpdateText FileTable.DataField @Pointer @Offset @Delete With Log @Bytes End Go Alter Procedure CleanUpBlob @Identity Numeric As Begin Set NoCount On Update FileTable Set PartiallyUploaded=0 Where $Identity=@Identity End Go Alter Procedure DeleteBlob @Identity Numeric As Begin Set NoCount On Delete From FileTable Where $Identity=@Identity End Go Alter Procedure RenameBlob @Identity Numeric, @FileName VarChar(250) As Begin Set NoCount On Update FileTable Set [FileName]=@FileName Where $Identity=@Identity End Go Alter Procedure FinalizeBlob @Identity Numeric, @Hash VarChar(250) As Begin Set NoCount On Update FileTable Set FileHash=@Hash Where $Identity=@Identity End
The main difference between this version and the version for SQL Server 2005 is the lack of a $IDENTITY object and the cap of 8000 bytes in a varbinary
CREATE TABLE [FileTable] ( [Id] [int] IDENTITY (1, 1) PRIMARY KEY NOT NULL , [FileName] [nvarchar] (50) NOT NULL , [DataField] [image] NOT NULL , [Partial] [tinyint] NOT NULL , [MimeType] [nvarchar] (50) NOT NULL , [Created] [datetime] NOT NULL CONSTRAINT [DF_FileTable_Created] DEFAULT (getdate()), [FileHash] [nvarchar] (50) NULL , ) GO CREATE Procedure CleanUpBlob @Identity Numeric As Begin Set NoCount On Update FileTable Set Partial=0 Where Id=@Identity End GO CREATE Procedure CreateBlob @Identity Numeric Output, @Pointer Binary(16) Output, @FileName VarChar(250) = null, @MIMEType VarChar(250) = null As Begin Set NoCount ON; Insert Into FileTable (Datafield,FileName,MimeType,Partial) Values ('',@FileName,@MimeType,1) Select @Identity = SCOPE_IDENTITY() Select @Pointer = TEXTPTR(DataField) From FileTable Where id = @Identity End GO CREATE Procedure DeleteBlob @Identity Numeric As Begin Set NoCount On Delete From FileTable Where id=@Identity End GO CREATE Procedure FinalizeBlob @Identity Numeric, @Hash VarChar(250) As Begin Set NoCount On Update FileTable Set FileHash=@Hash Where id=@Identity End GO CREATE Procedure OpenBlob @Identity Numeric, @Pointer VarBinary(8000) Output, @Size Int Output, @FileName VarChar(250) Output, @MIMEType VarChar(250) Output As Begin Set NoCount On Select @Pointer = TEXTPTR(DataField), @Size = DATALENGTH(DataField), @FileName = [FileName], @MIMEType = MIMEType From FileTable Where id = @Identity End GO CREATE Procedure ReadBlob @Identity Numeric, --ignored in this implementation, here for reference @Pointer Binary(16), @Offset Int, @Size Int As Begin Set NoCount On ReadText FileTable.DataField @Pointer @Offset @Size End GO CREATE Procedure RenameBlob @Identity Numeric, @FileName VarChar(250) As Begin Set NoCount On Update FileTable Set [FileName]=@FileName Where id=@Identity End GO CREATE Procedure WriteBlob @Identity Numeric, --ignored in this implementation, here for reference @Pointer Binary(16), @Bytes VarBinary(8000), @Offset Int, @Delete Int As Begin Set NoCount On UpdateText FileTable.DataField @Pointer @Offset @Delete With Log @Bytes End
HashedInputFile
web control and a custom UploadStorageProvider
called HashingFilesystemUploadStorageProvider
.
The HashedInputFile
control is a
subclass of InputFile
that adds a Hash
property which returns the cryptographic hash of the uploaded file
computed by the HashingFilesystemUploadStorageProvider
.
To use the HashedInputFile extension:HashedInputFile
control to your
toolbox. To do that,
right-click
on the Toolbox, click Add/Remove Items, Browse to NeatUpload-1.1.x/HashedInputFile/bin/Brettle.Web.NeatUpload.HashedInputFile.dll
,
click Open, and then click
OK. A reference to Brettle.Web.NeatUpload.HashedInputFile.dll
will
automatically be added to your project the first time you use the
designer to a HashedInputFile to a form.Brettle.Web.NeatUpload.HashedInputFile.dll
in the NeatUpload-1.
1.x
/HashedInputFile/bin
directory.HashingFilesystemUploadStorageProvider
.
The simplest way to do that is to add the following to your Web.config
(see Configuration
for more options):
<configuration>
<configSections>
<sectionGroup name="system.web">
<remove name="neatUpload"
/>
<section name="neatUpload"
type="Brettle.Web.NeatUpload.ConfigSectionHandler,
Brettle.Web.NeatUpload"
allowLocation="true" />
</sectionGroup>
</configSections>
...
<system.web>
...
<neatUpload
defaultProvider="HashingFilesystemUploadStorageProvider">
<providers>
<add name="
HashingFilesystemUploadStorageProvider
"
type="Brettle.Web.NeatUpload.HashingFilesystemUploadStorageProvider,
Brettle.Web.NeatUpload.HashedInputFile"
algorithm="MD5"/>
</providers>
</neatUpload>
...
</system.web>
...
</configuration>
algorithm
attribute above is passed to HashAlgorithm.Create().
The default value is "MD5", so the attribute could have been
omitted above.HashedInputFile
control from your Toolbox and drop it on the form.HashedInputFile
control to
your
aspx page wherever you
want the user to choose a file, using something like this:
<HashedUpload:HashedInputFile id="inputFileId" runat="server" />
InputFile
or HtmlInputFile
control.inputFileId.Hash
property to get a byte array containing the cryptographic hash.
You can also use inputFileId.HashSize
to get the number of bits in the hash (in case it isn't a multiple of
8).HashedInputFile/HashedDemo.aspx
and HashedInputFile/HashedDemo.aspx.cs
.
For a more complex configuration example, see HashedInputFile/Web.config
.
It uses location
filtering and is designed to work in conjuction with the Web.config
in parent directory.Progress.aspx
The Progress.aspx
page that comes
with NeatUpload is just an
example. You can modify it to suit your needs. For example,
you
could change the color scheme with minor modification to the
default.css
stylesheet that is included, or
you could change the text
or images that are used by modifying Progress.aspx
itself. You could
even rearrange the layout entirely or create a new page derived from ProgressPage
and use the Url
property of the ProgressBar
to refer to it. To allow AJAX refreshless updates, you must
include at least one DetailsSpan
or DetailsDiv
control. Those controls render as like div
and span
controls, respectively.
Use data-binding expressions within those
controls (either the in the content or properties) to access the
details of the upload at runtime. Below is a list of the
properties and methods that NeatUpload provides for use in data-binding
expressions. Refer to the default Progress.aspx
for an example of how each of these is used.
Name | Description |
Status |
A value from the UploadStatus
enumeration, as follows:Unknown - NeatUpload has not yet started
receiving the uploadNormalInProgress - a normal
(non-chunked) upload had started but not yet finishedChunkedInProgress - an upload which uses
chunked transfer coding has started but not yet finished.Completed - the entire upload
was successfully receivedCancelled - the user has cancelled the
upload using the cancel linkRejected - the upload has been rejected
by the server because it is unacceptable for some reasonFailed - an unexpected error occurred
while receiving the uploadIf the WhenStatus property of a DetailsSpan
and DetailsDiv control is set, the control
will only render when the status is one of the space-separated list of
statuses in the WhenStatus value.
If the WhenStatus property is not
set, the control will always display. |
BytesRead |
Number of bytes received by the server so far.
Pass this to FormatCount() to get a
more readable value. |
BytesTotal |
Total number of bytes in the upload. Pass
this to FormatCount() to get a more readable
value. For chunked uploads, BytesTotal
is -1. |
CountUnits |
The units associated with the result of a call to FormatCount() .
If BytesTotal is
greater that 1 million, returns the value of the MBUnits
resource. Otherwise, if BytesTotal
is greater than 1
thousand, returns the value of the KBUnits
resource.
Otherwise, returns the value of the ByteUnits
resource. Note: for chunked uploads, BytesRead
is used instead of BytesTotal . |
FormatCount(long count) |
Converts a number of bytes to units that would make BytesTotal
readable. If BytesTotal is greater
that 1 million, count is formatted
according to the MBCountFormat resource.
Otherwise, if BytesTotal
greater than 1 thousand, count is
formatted according to the KBCountFormat
resource. Otherwise, count is
formatted according of the ByteCountFormat
resource. Note: for chunked uploads, BytesRead
is used instead of BytesTotal . |
BytesPerSec |
Rate (in bytes/sec) at which the server has received
the
upload. While the upload is in progress, this is an average
over
the past 1-2 seconds. When the upload has finished, this is
an
average over the entire upload. |
FormatRate(int rate) |
Converts a rate to more readable units. If rate
is greater than 1 million, it is formatted according to the MBRateFormat
resource. Otherwise, if rate
is
greater than 1 thousand, it is formatted according to the KBRateFormat
resource. Otherwise, rate
is formatted according of the ByteRateFormat
resource. |
FractionComplete |
A double in the range 0.0-1.0. Computed as BytesRead /BytesTotal
for normal (ie non-chunked) uploads. Always 0.0 for chunked
uploads. |
TimeElapsed |
TimeSpan representing the time
that has elapsed since the upload began. |
TimeRemaining |
TimeSpan estimating the time
left until the
upload completes. This is projected based on the elapsed time
and
the fraction complete. Returns TimeSpan .MaxValue
if BytesRead is 0 or if this is a chunked
upload. |
FormatTimeSpan( TimeSpan
ts) |
Formats a TimeSpan for
readability using the following code:
string format; |
Rejection |
An UploadException (eg UploadTooLargeException )
when Status is Rejected .
Otherwise, null . It can
be useful to display Rejection.Message to the
user when a rejection occurs. NeatUpload throws an UploadTooLargeException
if the total request size is more than the size specified with the maxRequestLength
attribute of the <neatUpload>
element. The value of the UploadTooLargeException 's
Message property is based on the UploadTooLargeMessageFormat
resource in Strings.resx . |
Failure |
An Exception
when Status is Failed .
Otherwise, null . It can
be useful to display Failure.Message to the
user when a failure occurs. |
CurrentFileName |
The client-specified name of the file currently being received by the server. |
CancelVisible |
Whether the 'cancel' link should be visible. This is only 'true' when an upload is in progress and NeatUpload can use JavaScript to cancel an upload. |
StartRefreshVisible |
Whether the 'start refresh' link should be visible. This is only 'true' when the progress display is not refreshing and NeatUpload can't use JavaScript to automatically start refreshing it when the upload starts. |
StopRefreshVisible |
Whether the 'stop refresh' link should be visible. This is only 'true' when the upload is in progress, the progress display is refreshing, and NeatUpload can't use Javascript to cancel the upload. |
CancelUrl, StartRefreshUrl. StopRefreshUrl |
The URLs for the 'cancel', 'start refresh', and 'stop refresh' links, respectively. |
ProgressPage
calls it's GetResourceString()
method to retrieve all of it's resources. The default
implementation uses the embedded resources compiled from Strings.resx
.
If you want to use a different source, override GetResourceString()
in your subclass.
For AJAX refreshless updates to work, your ProgressPage
subclass must render as a well-formed XML document. Here are
a few tips to avoid the most common problems:
<body>
'
instead of '<BODY>
' or '<Body>
').
'
with ' 
'.<asp:HyperLink>
instead of <a>
for links,
especially for links that use CancelUrl
, StartRefreshUrl
,
and StopRefreshUrl
. Some HtmlAnchor
(i.e.
<a>
) implementations,
notably Microsoft's ASP.NET 2.0, do not HTML encode the value of the href
attribute. If the URLs contain ampersands, the XML will not
be considered well-formed unless they are encoded.DetailsDiv
and DetailsSpan
controls (like regular div
and span
controls) will not render as simple <div>
and <span>
elements in browsers
that ASP.NET considers "downlevel". Instead, div
controls are rendered as tables and span
controls may render with extra font tags, etc. The Machine.config
shipped with ASP.NET 1.1 contains a <browserCaps>
section that causes all non-IE browsers to be considered downlevel,
including Netscape, Firefox, Opera, Safari, etc. To force the
DetailsDiv
and DetailsSpan
controls to render as simple <div>
and <span>
elements even in
supposedly downlevel browsers, set the control's UseHtml4
property to "true". The default Progress.aspx
does that for the DetailsDiv
control with an
id of "barDetailsDiv" that is used to draw the gray progress bar.
Note that even with the UseHtml4
property it is still a good idea to update
or supplement the default ASP.NET 1.1 <browserCaps>
section to make the rest of your web application render more
cleanly in modern non-IE browsers.
ProgressBar
If JavaScript is not available or the browser doesn't support
IFRAMEs and the ProgressBar
element is empty,
a "Check Upload Progress"
link will be displayed. To change the text of that link, simply place
the desired text (or controls) between the begin and end tags of the
ProgressBar
element. For example:
<Upload:ProgressBar id="progressBar" runat="server" >
<asp:Label id="label" runat="server" Text="Your Text Here"/>
</Upload:ProgressBar>
FilesystemUploadStorageProvider
which streams
uploaded files to a
temporary directory until you move them. If you would rather
stream uploaded files to some other location (e.g. a database, or
remote machine), or you want some other functionality not provided
by FilesystemUploadStorageProvider,
you need to
create and use a custom UploadStorageProvider
.
To create a
custom UploadStorageProvider
, create a class
which inherits from
UploadStorageProvider
. You will need
to implement the following
methods:
public abstract void Initialize(string
name, NameValueCollection attrs);
public virtual UploadedFile
CreateUploadedFile(UploadContext ctx,
string
controlUniqueID,
string
fileName,
string contentType,
UploadStorageConfig storageConfig);
Initialize()
method,
passing the value of the "name" attribute as the first
parameter and the remaining attributes (other than "name", and "type")
as the second parameter. You can use the remaining attributes
to pass provider-specific configuration information to your provider.InputFile
control, it calls the
second
CreateUploadedFile()
method of the provider that is configured as the defaultProvider
for the requested page. When NeatUpload calls your provider's
CreateUploadedFile()
method, it passes the UploadContext
associated with the current request, the ID of the InputFile
control
associated with the upload, the browser-specified name and MIME
type of the file, and the value of the InputFile control's StorageConfig
property. CreateUploadedFile()
is responsible for
returning an instance of a class derived from UploadedFile
.
NeatUpload will stream the file to that object, and delegate
various InputFile
members to the associated
methods of your
UploadedFile
-derived object.CreateUploadedFile()
can
optionally use the ContentLength
property of
the passed UploadContext
to get the length of
the entire request, including all files. That information
could be used to reserve sufficient space to store the files, for
example. At the moment, no members of UploadContext
other than ContentLength
are available to
providers. Future releases of NeatUpload might use
UploadContext to pass other similar information to providers.UploadException
.
When NeatUpload detects such an exception, it:Status
to Rejected
and Rejection
to the UploadException
that occured)Application.Error
event or by
using custom error pages. UploadException
is a subclass of HttpException
, so you can
control which custom error page is used by specifying the corresponding
HTTP error code when you construct your UploadException
.UploadException
,
it assumes that it is an unexpected error, so it:Status
to Failed
and Failure
to the Exception
that occured)Application.Error
event and use custom error pages if they are configured.NameValueCollection
provided by UploadStorageConfig
is not sufficient for your provider, you can override UploadStorageProvider.CreateUploadStorageConfig()
to return your own UploadStorageConfig
subclass. Your subclass can override the UploadStorageConfig.Serialize(Stream)
and UploadStorageConfig.Deserialize(Stream)
methods to serialize itself to the provided Stream
and deserialize itself from the provided Stream
.
The base class implementations of these methods use LosFormatter
to provide a concise serialization of the NameValueCollection
as a Hashtable
.UploadStorageProvider
,
see FilesystemUploadStorageProvider.cs
and FilesystemUploadedFile.cs
.
For another example, see the files in the HashedInputFile
directory.Since users almost always use the file selection dialog, this issue almost never arises.
When ASP.NET tracing is enabled in the application Web.config, ASP.NET reads the entire upload before
NeatUpload can access it. As a result, NeatUpload can't stream the upload
to storage and can't provide a meaningful progress bar. Since NeatUpload can't do anything useful under such
circumstances, it automatically disables itself. Specifically, it acts as though you set the
useHttpModule
attribute of the <neatUpload>
element to "false". That prevents
display of the progress bar and prevents streaming to storage, but your code should otherwise continue to function
normally.
Note that this issue only arises when you enable application-wide ASP.NET tracing (i.e. via the
Web.config). It does not arise if you only enable ASP.NET tracing at the page level using the Trace
attribute of the <%@Page%>
directive. Unfortunately, if you enable
application-wide ASP.NET tracing but disable tracing for some or all pages using the
Trace
attribute of the <%@Page%>
directive, the problem still occurs. This is
because the Trace
attribute of the <%@Page%>
directive has no effect on whether
ASP.NET reads in the entire request before NeatUpload can access it.
ProgressBar
doesn't work with web gardens/farmsNeatUpload maintains information about the upload in the application state of the web application
receiving the upload request. Since application state is not shared across instances of the web
application, the ProgressBar
will not be able to determine the state of the upload if the
requests it makes go to a different instance of the web application. This can happen if you are running a
web garden (i.e. multiple worker processes) or a web farm (i.e. multiple web servers). To ensure that you
are not running a web garden, see Microsoft's documentation on
Configuring Web Gardens with IIS 6.
For high volume sites, you might be able to use a web farm if you are able to ensure that all requests from a
particular client are routed to the same server (i.e. sticky IP).
HttpResponse.AppendToLog()
does nothing when UploadHttpModule
is usedDue to the design of NeatUpload this bug will not be fixed,
however NeatUpload does provide a workaround. Simply replace Response.AppendToLog()
with Brettle.Web.NeatUpload.UploadHttpModule.AppendToLog()
.
That will work whether the UploadHttpModule
is used or not.
ProgressBars
don't work with OperaOpera does not seem to allow an IFRAME
to be updated
during form submission. NeatUpload works around this by using
a
popup progress bar whenever the User-Agent header contains "Opera"
(case-insensitive). Most browsers don't allow popups to be smaller that 500x100 pixels.
If you specify a smaller width or height or use units other than pixels, NeatUpload will automatically use the
minimum size in pixels.
If instead of selecting a file to upload via the file selection
dialog, the user types in a non-absolute path (e.g. just "x"), IE6 and
IE7 will not submit the form and no upload will occur. Under IE6,
it is possible to detect this situation and prevent the progress
display from starting. The situation is detected by using
Javascript to examine the value of the <input type="file">
form element. If the value doesn't look like an absolute path,
the progress display is not started. In IE7, the value of the
form element is always just the filename, not the full path, probably
for security reasons. As a result, NeatUpload can't prevent the
progress display from starting because it's not possible to determine
whether an absolute path was entered -- the value is "x" whether the
user entered "x" or "c:\x".
Since users almost always use the file selection dialog, this issue almost never arises.
When not using NeatUpload, an event handler returning false will prevent the browser from performing the default action
for the event. For example, consider a submit button with onclick="return confirm('Submit?');"
.
If the user clicks the button a dialog will appear asking "Submit?". When not using NeatUpload, declining will
prevent submission of the form. However, when using NeatUpload, the
form will be submitted if the user is using IE6. To workaround this, change such handlers to set
window.event.returnValue=false
when they return false. For example, here is the implementation
of a function that can be used instead of confirm()
in the above example:
function confirmSubmit(msg) { var confirmed = confirm(msg); if (!confirmed && window.event) window.event.returnValue = false; return confirmed; }
This problem is caused by a difference in the order IE6 calls event handlers. In order to prevent non-triggers
from starting an upload, NeatUpload registers event handlers on the <form>
element for a variety
of events (including onclick). Those handlers clear the filename if the event is not coming from a trigger. In
standard-conformant browsers (i.e. modern browsers other than IE), it is possible to register those handlers so that
they run before other handlers registered for controls within the form (e.g. submit buttons). As a result, the value
returned by the submit button handler is what determines whether the form actually gets submitted. In IE, the
NeatUpload form event handler runs last and the value it returns (always true) determines whether the form gets
submitted. NeatUpload can't determine the value returned by the submit button handler and return it instead,
so the above workaround is needed.
LinkButtons
do not start the progress display in Internet Explorer
for the Mac
NeatUpload overrides the form.submit()
JavaScript method to start the progress display when the
form is submitted programmatically (e.g. with a LinkButton
). Mac IE does not support overriding
form.submit()
so NeatUpload can't start the progress display if a LinkButton
is used to
submit the form. The upload still occurs though. Since Mac IE is no longer supported by Microsoft and currently
has less than 1% of the browser market, there are currently no plans to develop a workaround for this.
If you think NeatUpload's UploadHttpModule
might be causing a problem, the first thing you should do is set the <neatUpload>
element's useHttpModule
attribute
to "false" (or remove the UploadHttpModule
from the <httpModules>
section)
and try to reproduce the problem. All of your existing code
should continue to work - you just won't get progress bars and
streaming of files to disk. If the problem goes away when you
remove the UploadHttpModule
, it is a bug so
please report
it. You can help me diagnosis the problem by doing
the following:
debugDirectory
attribute
of the <neatUpload>
element in
your Web.config
to the name
of a directory in your webapp where NeatUpload can store copies of the
request bodies it processes. For example,
<neatUpload ... debugDirectory="MyDebugDir" ...>
would
cause NeatUpload to store copies of the request bodies in the
"MyDebugDir" directory under you application root.TestFilter.exe
program to reproduce how the UploadHttpModule
processed the request and (hopefully) determine exactly what went wrong.NeatUpload is capable of logging debug and error messages using log4net, but that capability is disabled by default to simplify installation and use of NeatUpload. To enable logging with log4net:
NeatUpload-1.1.x/log4net.config
to the root directory of your web application.Global.asax.cs
before
the namespace
keyword: [assembly:
log4net.Config.XmlConfigurator(ConfigFile="log4net.config", Watch=true)]
<assembly:
log4net.Config.XmlConfigurator(ConfigFile:="log4net.config",
Watch:=true)>