Controlling the Size of Uploaded Files

Jake Howlett, 19 June 2003

Category: Miscellaneous; Keywords: upload file size

If there is one subject that gets its fair amount of airtime on the Notes forums it's the matter of dealing with file attachments. Whether it's moving them to or from Rich Text fields, stopping them appearing at the bottom of every page, controlling the types of files that can be uploaded or limiting the size of uploaded files there is endless discussion.

In this article I am going to offer an approach to the latter of these issues - limiting the size of files.

The Problem:

There are lots of applications where you require the user to upload files. Even if you trust your users to only upload the files they should this doesn't mean they won't accidentally upload a huge file that puts extra burden on the server. Not to mention taken up extra space on the disk. What we need is a way to put a reasonably cap on the size of files that can be uploaded.

One method is to use the server document to limit the size of files that can be sent to the server by a form post. As below (note that 0 means NO limit):

Server post size setting

There are problems with this though. This limit will apply to all databases on the server. More than likely the limits you want to enforce will apply to different forms and be for different sizes.

And there's an even bigger problem. Imagine you set the limit to 1024KB (1MB) and somebody tries to upload an file that is 1.5MB. This is what they will see:

Error 500

Under no circumstance is it acceptable to have a user have to see this. What we need is a way to politely and elegantly inform the user that the file they uploaded is too big. We need some code.

The Solution:

Before I start I want to say that, as far as I am aware, there is no real way to test the size of a file from the browser. For the sake of this article I am assuming we are limited to the simple type="file" input element and no fancy plugins or whatnot. To check the size of the file we are going to have to upload the file to the server and then do our calculations there.

Because of the way things happen in Domino and the way it processes submitted forms we are going to have to do out coding in the Web Query Save event. The reason for this is that it's the last thing that happens and we can be sure that all the files are in place and ready for inspection.

In coming up with a solution I tried two approaches. First with some simple @Functions and then with LotusScript code. The reason that I moved on to LotusScript was that the @Function method is not really something I would recommend. I'll explain why at the end of the @Function section. If you want to skip this to the better method then go straight to the LotusScript section now. Otherwise, if you want to skip all of my rabble and just download the attached example database, it's here.

Using @Functions:

It's pretty obvious that we can do it in LotusScript. You can almost everything in LotusScript. You don't always have to though and I always like to have a go with @Functions before I start Dimming things. All we need in this case is to check the size of the biggest attachment which we can get at using @AttachmentLengths. If the biggest file is bigger than our limit we set the SaveOptions field to "0" (means the document is not saved) and set the value of $$Return (where the browser gets redirected to) to a URL that ends with a keyword that will cause a warning message to display. Here's the code I came up with.

@If(

@AttachmentLengths = "";

@SetField("$$Return"; "[/" + @WebDbName + "/0/" + @Text(@DocumentUniqueID) +"?OpenDocument]");

@If(

@Max(@AttachmentLengths) > 1000000;

@If( @IsNewDoc;

@Do(

@SetField("SaveOptions";"0");

@SetField("$$Return"; "[/" + @WebDbName + "/formula?OpenForm&FileTooLarge=True]")

);

@Do(

@SetField("SaveOptions";"0");

@SetField("$$Return"; "[/" + @WebDbName + "/0/" + @Text(@DocumentUniqueID) +"?EditDocument&FileTooLarge=True]")

)

);

@SetField("$$Return"; "[/" + @WebDbName + "/0/" + @Text(@DocumentUniqueID) +"?OpenDocument]")

)

)


This code goes directly in to the Web Query Save event as below. Yes, you can do this!

Web Query Save Code

It might all look a little complex but it's really dead straightforward. There is just one major problem with it. The fact that we set the SaveOptions field to "0" means that the document is not saved. This might be acceptable if they were only uploaded a file but what if they made changes to a load of text fields as well. All their changes will be lost and you have an unhappy user on your hands!

This might be okay in your situation so, in that case, go ahead and use it. It's a lot easier than coding agents in LotusScript all the time.

Using LotusScript:

If ever you're going to do something like this LotusScript is more than likely going to be your first choice. And rightly so. The thing I found is that the most obvious route is not always the one that works. My first attempt used a loop through all the doc.EmbeddedObjects. Let's just say I had to give up on this. As I'm always saying, Notes has its problems with attachments. Instead I had to Evaluate @AttachmentNames in the script and get each one of these by name. Here's the code. I've simplified it a lot for the sake of presentation. The full code can be found in the database attached.

redirect = "[/" & dbWebPath & "/0/" & doc.UniversalID + "?OpenDocument]"
If doc.HasEmbedded = True Then
files = Evaluate(|@AttachmentNames|, doc)

Forall v In files
If v <> "" Then
Set obj = doc.GetAttachment( v )
If obj.FileSize > doc.DocFileSizeLimit(0) Then
Call obj.remove
If doc.IsNewNote Then
redirect = "[/" & dbWebPath & "/script?
OpenForm&FileTooLarge=True]"

Else
redirect = "[/" & dbWebPath & "/0/" &
doc.UniversalID +
"?EditDocument&FileTooLarge=True]"
End If
End If
End If
End Forall
End If
Call doc.ReplaceItemValue( "$$Return", redirect )


My testing hasn't been exhaustive but this seems to be the best approach overall. At least with this method the document is saved along with all the changes the user may have made.

Informing the user:

You'll notice in both the examples that we made changes to the %%Return field's value. The value of this field dictates where the browser is redirected to after the document is submitted. Notice that when the file was too large we added an extra something to the URL (&FileTooLarge=True). We can then get the form to check for this parameter each time it's opened. If it's there we display a message about there file having not being uploaded. Here's the code:

Message

Notice the code is in the red Computed Value section just below the file upload control. Here's what the user sees:

Message

A lot better than seeing an Error 500 message, I'm sure you all agree.

Summary:

Hopefully you've got an idea for a better way to limit the size of files that users can upload. It's not perfect but then not many solutions in Notes are. One problem is that although the files are removed before the document is saved they have still been added to the database already. This means that you end up with a lot of empty space and a database in desperate need of a compact.

If the LotusScript I talked about before I might be able to investigate using the SaveToDisk property of the NotesItem ("$file") that represents the file. Alas, unless somebody out there knows any different, you can't. I left the code in the agent if you want to try and fix it.

Note: I've used some R6 only @Functions in this article and the attached database will have an ODS of 43. That's not to say you can't work it in R5. You should now how to mimic @WebDBName in R5 and getting URL parameters is discussed in this article.