logo

Controlling the Size of Uploaded Files

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.

Feedback

    • avatar
    • Seth
    • Thu 19 Jun 2003

    @URLQueryString ?

    Hi Jake,

    Great article, enjoy your site/blogs. I have never seen @URLQueryString. Is that an R6 command? I usually just use the Query_String or the Query_String_Decoded cgi variable and parse it with @Contains or @Middle... -Seth Richmond, VA USA

      • avatar
      • Seth
      • Thu 19 Jun 2003

      Re: - just saw your note about r6 @functions (eom)

      • avatar
      • Jake Howlett
      • Thu 19 Jun 2003

      Re: @URLQueryString ?

      Hi Seth,

      Indeed it is an R6 thing. There are a few new URL-specific functions added:

      @URLDecode @URLEncode @URLGetHeader @URLSetHeader @URLHistory @URLQueryString

      Jake

  1. Nice work, one correction though.

    Jake,

    This is a common requirement in the Domino.Doc world where files can be huge and so is the upload time. I usually write a bit of VBScript to check the file size before the file is uploaded when the environment allows me i.e. using a IE browser and having the security settings set correctly. I have previously detected large files and created popup a message indicating that maybe they should optimize there embedded graphics. But in the Domino.Doc world you dont prevent a user from uploading files that the purpose of the system.

    By the way your comment that "note that 0 means NO limit" for the Maximum POST data limit is incorrect. See this link for clarification:

    http://www-10.lotus.com/ldd/nd6forum.nsf/55c38d716d632d9b8525689b005ba1c0/e62f65 1dc7b85d4485256c84005f783c?OpenDocument

    regards john marshall

      • avatar
      • Jake Howlett
      • Thu 19 Jun 2003

      Re: Nice work, one correction though.

      Nice work Lotus!

      So, in summary:

      "Originally in 5.0 the default was unlimited (specify a zero) then in 5.0.11 this was changed so that a setting of zero really meant 32 meg."

      "Now in R6, a setting of zero 0 means default to 10 meg - so that if you do not specify a larger number then the most that is postable is 10 meg."

      What are they thinking about over there?

    • avatar
    • Greg W
    • Thu 19 Jun 2003

    Notes R5

    I know you've just upgraded to Notes 6 but what about the rest of us still using R5, we can't access your examples.

      • avatar
      • Jake Howlett
      • Fri 20 Jun 2003

      Re: Notes R5

      Come on Greg, it's beed out for almost a year now. Isn't it about time you downloaded the clients and installed them on your PC?

      Show the rest of this thread

      • avatar
      • Charlotte
      • Fri 21 Nov 2003

      Re: Notes R5

      I'd agree with Greg on this one. Not *all* companies can just upgrade to R6 with a flick of the finger.

      Usually, they want a *stable* platform. No one wants to be interrupted by the downtime of the servers. It means a lost of money, sometimes in billions for some.

  2. Setting maximum POST data per WebSite

    One more note. If you use the new "Internet Site Documents" in R6, you can/have to set the maximum POST data per web site documument.

    Still, it's a good idea to prevent users from seeing a stupid error page only.

  3. Test the file size from the browser using JScript

    There is another way to check the file size through Jscript, using the ActiveXobject FileSystemObject.

    Regards David Schmidt

    <HTML> <HEAD> <SCRIPT LANGUAGE="JavaScript"> <!-- function getfilesize(filepath) { var filesyst=new ActiveXObject('Scripting.FileSystemObject'); var f=filesyst.GetFile(filepath); s="<b>File Name: </b>"+f.Name+"<br />"; s+= "<b>File Size: </b>"+ f.size+"<br />"; s+= "<b>Path: </b>"+ f.Path.toUpperCase()+"<br />"; s+= "<b>Type: </b>"+ f.Type+"<br />"; s+= "<b>Created: </b>"+ f.DateCreated+"<br />"; s+= "<b>Last Accessed: </b>"+f.DateLastAccessed+"<br />"; s+= "<b>Last Modified: </b>"+f.DateLastModified; document.all.dspfileprop.innerHTML=s; } // --> </SCRIPT> </HEAD> <BODY TEXT="000000" BGCOLOR="FFFFFF"> <FORM METHOD=post ACTION="/david/test.nsf/fileprop?OpenForm&Seq=1" ENCTYPE="multipart/form-data" NAME="_fileprop"> <INPUT ID="fileupload" onchange=getfilesize(fileupload.value); TYPE=file NAME="fileupload"> <div id=dspfileprop style="border:1px solid black; padding:5px"></div> </FORM> </BODY> </HTML>

  4. Attachments from a browser

    Getting the names of attachments submitted via a browser is a pain, because the only way to get them is by name, which is what you're trying to determine!

    As an alternative to doing an evaluate to get the attachment names you can do this:

    Forall itmField in doc.Items if itmField.Type = ATTACHMENT then 'itmField.Values(0) is the filename 'itmField.ValueLength is the file size end if end Forall

    A bit inefficient but it does avoid having to load the formula interpreter to process the evaluate command.

      • avatar
      • Jake Howlett
      • Mon 7 Jul 2003

      Re: Attachments from a browser

      Thanks for the feedback Dorian. You're right in what you said. It would be more efficient to do it using your code but if you look at the bottom of the agent you'll see some commented-out code that describes why I didn't do it this way.

      Basically, there's a bug. Quel surpris.

      Jake

      Show the rest of this thread

  5. Does submission method matter?

    Jake,

    Does the method I use to submit my form matter? I have a couple of things I'm doing in my form that are different than your example form. I have tried the code and it works in your example, but not when I put it into my form..

    Here are two of the differences in my code. - $$Return is computed for display - I submit with JavaScript: form.submit()

    The way my $$Return works is that it is computed for display and depends on another field.. like the following.

    simplified code for $$Return ----------------- @If( redirectField = 1 ; "[url1]" ; "[url2]" );

    So... In my javaScript function that does the submit, I alter "redirectField" to 1 or 0. Then I perform a form.submit() and everything is peachy. Can you see the reason why your code won't work on my form?

    Best regards,

    K Ford

  6. One other issue. Slightly related

    First, I want to thank you Jake for putting together one heck of a site. I've gotten a lot of ideas from this site and it has helped me greatly.

    I figured out my problem (from previous reply) was that my $$Return was computed for display. I changed it to computed and it is working fine.

    I have one other thing I can't figure out, though. When I set the $$Return field and the browser reloads (either telling the user the upload failed or succeeded), I'd like to include a named anchor in the URL so it will go down to where my file upload table is (way down in the form).

    I tried to add that to the webQuerySave agent (from your example DB) and it did not work.

    I did this (where #namedLink existed on the form)...

    Call doc.ReplaceItemValue("$$Return", "[/" & dbWebPath & "/0/" & doc.UniversalID + "?EditDocument&FileTooLarge=True#namedLink]")

    When the document actually reloaded, the #namedLink was NOT at the end of the URL. Any thought's or ideas on this one?

    Thanks again,

    K Ford

      • avatar
      • cuschman
      • Thu 8 Jul 2004

      Re: One other issue. Slightly related

      To answer your issue of the missing named anchor.

      I am having the same problem but I dont know why its not reaching the browser. D-oh.

      At this point i pass the name via url parameter back to the browser and have onload event reset the documents href property, appending the target name as a standard '#name' anchor.

  7. Modified the Authors field formula

    Thanks for your article.

    Note: I had to modify the Authors field formula into @Username in stead of the predefined "Anonymous".

    It was almost a copy and paste solution ;-)

    Kind regards,

    Patrick Kwinten http://www.quintessens.com

  8. the Formula is just as good...

    If the fields are reset, why dont you pass the variables in the URL and back to the form?

    If you do that, the formula method is way better than lotusscript.

    cheers TS

    • avatar
    • Niel Revelle
    • Mon 8 Mar 2004

    Content_Length

    I just tested the following on R5:

    Create Content_Length as a hidden editable field with a default value of Content_Length. Use the following for a Validation Formula:

    @If(@IsError(@TextToNumber(Content_Length));@Success;@TextToNumber(Content_Lengt h)>1024;@Failure("Your post is too large ("+Content_Length+")");@Success);

      • avatar
      • Jake
      • Mon 8 Mar 2004

      Re: Content_Length

      Didn't think of that. I presume you've tried it and it works?

      BUT, what if there are 3 file uploads on the form? Each allows a 10kb file.

      Content_Length also includes all the text fields on the form as well.

      Nice idea but not a solution you would catch me using.

      Jake

      Show the rest of this thread

  9. Thank You

    Just a big thank you for that template. It works very fine and is easy to implement.

    Thumbs up !

  10. thank u

    hi

    your script makes my task simple.. Am trying to write it in java script but i have some problems. can u help me any way..

    • avatar
    • S Hu
    • Wed 13 Feb 2008

    To not save the doc...

    I'm doing my first web app with Notes, which involves dblookup (thanks to prototype.js and AJAX! =) ) and attachments.

    This is a great solution!

    I didn't like the fact that the doc still gets saved if the file size exceeds the limit, so I added a computed field with the value:

    @SetHTTPHeader("Expires";0)

    and in the WQS agent, I mod it so that it adds the total files being uploaded (I have 5 File Upload Controls) and if the total exceeds the limit, then I return a "/FileSize?OpenForm" page where I think have a history.back(-1).

    This will then allow the user to go back to the previous page without losing any text that the user has typed in.... and the doc isn't saved in the back in too :)

    I hope this info helps others.

      • avatar
      • S Hu
      • Wed 13 Feb 2008

      Re: To not save the doc...

      ugh.. should have proof read my post beforehand!

      should read: "...where I have..."

      and not: "...where I think have..."

      • avatar
      • Jake Howlett
      • Wed 13 Feb 2008

      Re: To not save the doc...

      Hi S Hu,

      I'm confused and intrigued.

      How does the Expires header stop the document saving?

      Jake

      Show the rest of this thread

Your Comments

Name:
E-mail:
(optional)
Website:
(optional)
Comment:



Navigate other articles in the category "Miscellaneous"

« Previous Article Next Article »
The DomBlog Template v1.6   A guide to the Domino 6 upgrade

About This Article

Author: Jake Howlett
Category: Miscellaneous
Keywords: upload; file; size;

Attachments

fuclimit-v1.0.zip (87 Kbytes)

Options

Feedback
Print Friendly

Let's Get Social


About This Website

CodeStore is all about web development. Concentrating on Lotus Domino, ASP.NET, Flex, SharePoint and all things internet.

Your host is Jake Howlett who runs his own web development company called Rockall Design and is always on the lookout for new and interesting work to do.

You can find me on Twitter and on Linked In.

Read more about this site »