1,804 articles and 15,556 comments as of Tuesday, December 28th, 2010

Friday, January 22, 2010

SharePoint List Attachment Technical Brief

Guest Author: Michael Greene
Hyper-TechDesign

I recently found myself searching (to no avail) for information on how the SharePoint list attachment process actually works; it seemed as if there was no documentation or previous experiences available anywhere. In my particular scenario, I needed to be able to produce an auditable list which showed the date/time, filename, and username for every upload, a report which WSS doesn’t offer out of the box.

My solution became the creation of a second list, which would serve as this master record of all uploads. Once I understood how SharePoint actually processes the file attachments, I was able to use a combination of the PreSaveAction function and the jQuery Library for SharePoint Web Services to log each of the file names into my list.

This article isn’t meant to address the complete breadth of capability, but more to offer the technical ins and outs. I should point out that for the purposes of the article I’m making the assumption that you’re working on the default NewForm.aspx or EditForm.aspx forms. While I would imagine the document model and process is very similar for custom list forms, I have yet to dive into that world.

Where is the attachment form?

You may (or may not) be surprised to learn that the attach file form actually exists right inside your stock list form. The following graphic breaks out the overall form structure—more on the details in a minute. For the purpose of this article I’ve created a test list with 4 fields.


  1. All of the list fields and their submit buttons exist inside a span with an ID of “part1”.
  2. The “idAttachmentsTable” is populated dynamically to display the paths, names, and delete icon for each attachment.
  3. The file fields and related submit buttons exist inside a span with an ID of “partAttachment”. This is hidden by default so it is not viewed when the form is initially opened.
  4. The “attachmentsOnClient” table contains all of the file fields for the form. The contents of this table are always hidden to the user.


Clicking the “Attach File” link at the top of the form calls a JavaScript function that has two tasks. First and foremost it checks to make sure that the form structure (sections 1 and 3) exist. Assuming that check passes it then hides section 1, reveals section 3, and sets the focus to the active file field—creating an illusion to the end user of a new form loading.

What actually happens when I attach a file?

When you select a file for attachment and click OK, you’re not actually sending the files to the server yet. SharePoint keeps track of all of your changes to the list fields, attachment of files, deletion of files, etc., then waits until you save the overall list entry to transfer any new files to the server.

SharePoint manages a numerical variable called “FileUploadIndex” which it uses to keep track of how many attachments you have, and ultimately translates into the names of the file fields that you interact with to supply your attachments. The ID for each file field becomes “fileupload” concatenated with the current “FileUploadIndex” (ie: “fileupload0”). I’ll point out now that Microsoft is very inconsistent with field and variable lexicon here; sometimes they use “FileUpload” and other times it’s “Fileupload”, etc.


When you click the OK button to attach your file, SharePoint calls the “OKAttach” function that performs the following tasks:

  1. First it creates a new row in Section 2 containing the file path and the link to delete the file, should you wish to suddenly change your mind.
  2. Next it hides the file field you used to select your file, creating the illusion that it now sits inside Section 4.
  3. Thirdly it creates a new file field inside Section 3 incrementing 1 to the “FileUploadIndex”. So for example, if you had just uploaded your first file (fileupload0), this step would create a new file field inside Section 3 called fileupload1.
  4. Finally, it hides Section 3 in its entirety and displays Section 1, creating the illusion that the other form has now loaded again.

Should you now choose to click “Attach” again to add another file, you’ll actually be populating the new field (ie: “fileupload1”) created in step 3, and the process repeats. When the attach form is displayed for your next file, only the active field (essentially the one with the highest index) is displayed, while all others are hidden inside Section 4.

So what does this get me?

Let’s say for example we wanted to check file extensions to prevent certain file types from being uploaded, or possibly make a web service call to see if documents with the same name have already been added to a document library or another list. In my particular use, I was making a web service call to log the names of the files in another list. Due to the fact that SharePoint names the file fields with a constant prefix, we can access those fields and run our own script or validation against them.

You can either use straight JavaScript or jQuery to access the file fields. In my particular case I had already loaded jQuery so it makes sense to utilize that method. The following example code loops though all fields that have a name starting with “fileupload”, outputting the full local path and filename.

<script type="text/javascript">
function PreSaveAction() {
$("input[name^='fileupload']").each(function() {
if ($(this).val() != "") {
alert($(this).val());
}
});
}
</script>

You’ll notice that I’ve placed my alert inside an “if” statement that checks to make sure the value isn’t “nothing”. Remember that step 3 of the OKAttach call creates a new file field ready to accept the next file. As a result of this, there will always be a file field on the form that has no file in it. Obviously when you’re processing the files you don’t want to be bothered with blank rows, so a simple check to make sure the value isn’t blank, or the length of the string is greater than zero will suffice here.

If you wanted to record the file name to another list, like in my application, you can utilize the jQuery Library for SharePoint Web Services to call the UpdateListItems operation of the Lists web service and record $(this).val() into your list.

Conclusion

My particular scenario concluded with accessing the file names the user had selected, utilizing PreSaveAction to call the UpdateListItems web service and log the selected names to another list. That said, the true potential of understanding the process surrounding list attachment process is to couple it with the attachment deletion process. In my next article, we will explore how SharePoint tackles deleting files within the form and how to integrate the two processes to prevent people from uploading file types that you specify.

Guest Author: Michael Greene
Hyper-TechDesign

Michael Greene is the founder and president of Hyper-TechDesign, based in Binghamton, NY. With over a decade of experience in the internet development arena, Michael specializes in helping customers adopt and integrate technology to increase efficiency and foster improved collaboration, customer interaction and communication. He is also a frequent participant in SharePoint discussions on Twitter, and operates his personal blog, mike-greene.com.

 

Please Join the Discussion

49 Responses to “SharePoint List Attachment Technical Brief”
  1. Nice article, Mike, and congrats on your first here! I look forward to the next one. (This feels a little like book authors providing quotes for each other’s book jackets.)

    M.

  2. Zacharias says:

    Very cool!

    Now, why is it that SharePoint does not allow you to load a picture the same way eBay or Craigslist do? (tried to create a bidding SharePoint site).

    The old way: You (the end user) have to load the image to a library, grab its URL and then feed it to your SPList field? no, no, no.
    The new way: just attach the image file to the SPList item and put its path on a second field of type image.

    Thanks to @SpJeff for the help on this one.

    I ended up hijacking the OK button on section 3, adding my own function to read the file path on section 2.

    This one iterates through all the fields just like jQuery example:

    function grabShortFilename() {
        for (var i=0; i&lt;9; i++) {
            try {
                 var shortFilename = document.getElementById(&#039;fileupload&#039;+i);
                } catch (e) {}
            if (shortFilename != null) break;
            }
    
           var splits = shortFilename.value.split(&quot;\\&quot;);
           shortFilename = splits [splits .length-1];
           document.getElementById(&#039;YourTextFieldID&#039;).value=shortFilename;
    }
    
    var obj=document.getElementById(&#039;attachOKbutton&#039;);
    obj.onclick = function(){grabShortFilename(); OkAttach();};
    

    HTH

  3. Shalin Parmar says:

    Excellent article Mike! I also wanted to know about the attachemnt structure in lists and this helped me a lot. I hope somehow MSFT fixes the Create List Item or reltated actions in SPD workflow to allow copying of attachements to another list.

  4. madan says:

    Thanks for giving this nice article. I have to work on the attachment image files. I need to show this in grid with thumb image of the attached image. Could you please help how to do it in sharepoint webpart.

    Thanks in advance.
    madan.

  5. Karen says:

    I agree with Shalin. It would be awesome if the attachments automatically populated an associated document library.

    • Brian Bedard says:

      The attachments are stored in a hidden folder called Attachments on the List root folder in a subfolder named the item number. The attachment retains its filename so it’s easy to retrieve through the API and maybe the Web browser.

  6. I’ve always wondered why there is no attachment processing capability in the SPD workflows itself. I’m not sure if this is fixed in 2010, but it’s a feature that would certainly be worth having. I’ve also had no luck getting the Copy web service to function with attachments on a list; it works great for moving things between document libraries though.

    I didn’t cover it in the article, but when SharePoint physically transfers the attachments to the server, they’re placed /Lists/LISTNAME/Attachments/ID/filename.ext, where ID is the identifier of the list entry. So they’re not in an easily accessible document library, but they are structured in a way that you can easily reference them if you know the list name and record ID.

    @madan, there’s a few ways to accomplish what you’re trying to do. You can build a Data View Web Part with some custom XSL to build your grid, or you can use the jQuery Library for SharePoint Web Services (http://spservices.codeplex.com/) to query the attachment collection and build your grid with jQuery. If you go the DVWP route, Marc just started a new series all about XSL structure, which is excellent (http://www.endusersharepoint.com/2010/01/19/unlocking-the-mysteries-of-data-view-web-part-xsl-tags-part-1-overview/) and would be a great place for you to start.

  7. Brian Bedard says:

    Good article. But one point remains unclear. When do you call the auditing function? Is it safe to assume if PreSaveAction is called, the files will indeed get uploaded? Why didn’t you want to do the audit on the server side?

  8. Brian,

    When you call it is entirely up to you, based on what you’re trying to do. In my case, I was logging the names of each attached file to another list. If you wanted to audit file types, you could also do this within PreSaveAction (and we’ll look at that in my followup article).

    Anything that happens in PreSaveAction happens before the files are transferred. When PreSaveAction is called, it returns a boolean result which tells the SharePoint form to submit or not–it is at that point that the files are transferred to the server.

  9. Erucy says:

    Nice article, I just researched on the attachments part in the list form. Because I meet a problem about attachement. Just like you said, the entire attachment part is render from js, so when I refresh the page, or click “OK” but some fields invalid, all the attchments I selected is gone…….

    I discovered the reason, but I can’t find a easy workaround to solve this problem. Do you have any ideas?

  10. Ishak says:

    Any way to only allow 1 file attachment for each list ?
    Of course this can be done by using custom webpart, but if i just want to modify existing form using SPD, how the work around ? Any suggestion ?

  11. @Erucy, what exactly are you trying to do that’s forcing you to refresh the page as after the attachments have been selected? If you can give some more information we can discuss workaround options for you.

  12. Erucy says:

    Well, it’s a simple scenario. Just create a “Custom List” (which attachment is enabled by default). New Item, select some attachments, but leave the “Title” field empty (which is required by default), and click OK. There should be a “This field cannot be empty” error below the Title field, but all the attachments is gone.

  13. Have you implemented a PreSaveAction() function in your list? In my experience, if you have a PreSaveAction() function, SharePoint ignores its own field validation, so you should include validation to make sure those required conditions are met in PreSaveAction(). Otherwise it’s not until SharePoint physically submits the list that it determines that required conditions aren’t met, and by that time it has lost your attachments.

  14. Erucy says:

    You mean implement validations all by javascript, before the page is post back? Well, in most cases it’s not difficult, but in the case I did some days before, it’s not easy. Actually, I need to validate calendar conflicts (include recurrence event, it’s terrible….) in the new form page, so I redefine a Save button……

    I had a short discuss with my guys, we think we can only make another attachment upload page, after the item is created.

  15. @Ishak

    There’s two parts to this, the new form and the edit form.

    For the new form, you could use some code to read the fileupload0 field and see if it’s populated, and if so, use jQuery to hide the “Attach file” link–so essentially you’d only allow a user to attach one file to the list.

    On the edit form, you’d then have to check the idAttachmentsTable to see if there is anything in it, and if so hide the “Attach file” link–so if there was something previously uploaded hide the link. You’d also need the same provisions for the new form, in the edit form, in case the first time they’re adding a document is when they edit the form.

  16. @ Erucy

    So you’re working on a custom list form, correct? Not the default SharePoint form?

    You can decide if you want to create a PreSaveAction() function or not, but if you do, you skip over SharePoint’s internal data validation. PreSaveAction() is fired when the user submits the form, and it returns a boolean true or false which tells SharePoint if it can submit the form or not.

    So you would have a PreSaveAction() that would do your date calculations and such, and return true if conditions are met, or false if conditions aren’t. PreSaveAction() is called before submit/post, so you don’t loose any of your attachments by doing it that way. It sounds like you’re using a custom form that posts before validation. You need to do the validation client-side in a PreSaveAction() function if you want to keep the attachment integrity.

  17. Erucy says:

    Umm…I used default list form, only the Save button is customized (there’s not much time to build a custom form). I called SaveButton.SaveItem to save the form in new and edit form.

    I’ve tried the FileUpload control, every time after I do a post back, the file is missing…. Maybe there’s something wrong with my code. I’ll take some more experiments tomorrow. (It’s late in the night here, I’m in China :P)

    Anyway, thank you very much. :)

  18. @Erucy

    If you modified the Save button, it’s quite possible it’s not looking for PreSaveAction() anymore, so I’d check there.

    The normal logic of a form submission is: User Submits Form -> SharePoint executes PreSaveAction() (return boolean true or false) -> If PreSaveAction() == true, form submit.

    So there is no post action at all until validation has been confirmed; sounds like a bug in the form submit code.

  19. @Erucy

    Just to throw out another thought, why don’t you just do all of your validation within PreSaveAction()… there’s not really a need to modify the save action at all, when SharePoint already gives you provisions for writing your own validation code.

  20. Erucy says:

    It sounds great. But I wonder if I can read recurrence info from javascript in a short time and find the conflict in the whole calendar (currently I use a Updating event receiver from CodeProject, it takes 10+ classes to deal with the recurrence data…) But maybe I can build a web service and use ajax….

  21. Brian Bedard says:

    You might want to go a different route. Have you heard of RenderingTemplates? If you have access to the 12 hive and can build and deploy files to the file system. You can override the listform template and the attachment upload template. You can add a piece of JavaScript that would prevent the end user from uploading more than one attachment. The rest of your code and Sharepoint code can stay intact. The RendingTemplates are VERY modular. You might even be able to override the SaveButton (RenderingTemplate) and put in your code. RenderingTemplates generate the List Form Web Part so you don’t have to hijack the form with jacascript and break the internal workings of the web part.

    But this will only work if you can deploy files including the list schema. Read my post on this stuff.

  22. Giuseppe says:

    Great article and it was super helpful! I’m interested in promoting the attachment field in the order on my display form – I’d like to make it appear first or at the top of my page. I also picked up on the attachment being stored at /Lists/LISTNAME/Attachments/ID/filename.ext but am not sure how to call it on my display form. I’d certainly appreciate your help in pointing me in the right direction, thanks!

  23. Giuseppe says:

    Thought I would follow up with the solution to the question I posed yesterday. Inserted the following into the code view of SPD and it worked like a charm:

  24. @Giuseppe

    Glad you got everything working. Mark is off at SPTechCon with many of the other who’s who of SharePoint, but he may be able to amend your comment later to include your code for the discussion. One option (and this may be what you did) would be to use some JavaScript to grab the contents of the attachments table and output it elsewhere in your form.

    Did your solution also solve your question about calling the attachment location, and if not, could you possibly elaborate a little more on what you’re trying to do and I’ll try and get you going in the right direction.

    I’m glad this article has created so much discussion. My sincere apologies for being behind in getting the followup one out there; things have been very hectic over the last couple weeks but I promise it is coming.

  25. Dan Sharpe says:

    This is a great post – what I am attempting to do is not manage the upload of attachments, but instead audit who downloads the attachments which are attached to specific items in the server lists.

    • Dan,

      I have run into this need a couple times and haven’t found a good solution to be honest. The best way I could solution this was to create a log list, and write my own function to hijack the hyperlink you click when you open the attachment. That function then makes a web service call to write the document name and username into the log list.

      It’s a clunky solution, but I have yet to come up with a better one–if you do, please share!

      Mike

  26. Shankar Raman says:

    Great Post MG- got a question for you. I am trying to create a custom application page that pretty much looks the same as the NewEditForm.aspx and adding a custom spell check to the submission process so that all the text fields can be properly spelled ( we are using WSS 3.0).
    I figured all of how SharePoint uses javascript to show the attachment section and add file location to the bottom of the screen. The one thing that I am not able to figure out is how do I upload the attachments using server side script to the list.

    • Brian Bedard says:

      Once the PreSaveAction runs and attachments are submitted, they live in memory in the Request.PostedFiles collection. And the item has an Attachments collection. If you want to manually save attachments in the item, you have to transfer them from the PostedFiles to the Attachments. There is an Add method that takes a filename and a byte array. The PostedFiles collection has HttpPostedFile objects with a Stream property to retrieve the byte array.

      If you do nothing they will just save into the item for you probably through the same process.

      When I did this I was overriding SaveItem in the SaveButton control using RenderingTemplates.

  27. Shankar Raman says:

    Hi Brian,
    Thanks for your response. So did you create a custom “SaveButton” control and added it to your application page?

    • Brian Bedard says:

      Yes in a way. I didn’t use an application page, I used the out of the box EditForm.aspx list form web part page.

      When using Rendering Templates, every control can be overriden and you can create a supporting class for it. When you do that, you just subclass SaveButton and override the methods like SaveItem. I used my “new” SaveButton to control field level write access to my list item.

    • Shankar,

      My apologies, it seems my notification was turned off and I didn’t realize you’d posted.

      Brian’s approach will work for attaching documents behind the scenes. Your other option is to use the web services to push a new attachment into a collection; although I’ve run into some memory issues with that when encoding large files client-side with Javascript.

      Hope you got everything sorted out, again sorry for the lack of response.

  28. Shankar Raman says:

    Hi MG,
    No need to apologize. I did manage to get this working thanks to Brian’s advice. I appreciate your post and getting back to me.
    Thanks,
    Shankar.

  29. Mark Wheeler says:

    I just can’t seem to get this to work…

    1. Find filename of attachment.
    2. Save filename to second list.

    function PreSaveAction() {
    	 // alert("PreSave");
    
    $("input[name^='fileupload']").each(function() {
    		if ($(this).val() != "") {
    			alert($(this).val());
    			var fpath = $(this).val();
    			var m = fpath.match(/(.*)[\/\\]([^\/\\]+\.\w+)$/);
    
    			alert(m[2]); 
    
    	}
    	});  
    
    $().SPServices({
            operation: "UpdateListItems",
            async: false,
            listName: "WorkInfo",
            updates: "" +
                    "" +
                    "" + m[2] + "" +
                    "" +
                "",
            completefunc: function(xData, Status) {
             alert("Status=" + Status + " XML=" + xData.responseXML.xml);
            }
        });
        return true;
    }
    
    • Mark,

      If you’re trying to create a new record with that SPServices call you’re missing some stuff. You need to specify Cmd=New and pass a query string. Is your alert spitting out the correct file name? If so, you may want to take this over to spservices.codeplex.com for help with adding the new records and we can help you there.

      Mike

  30. Charlie Epes says:

    Hi Michael:
    I’m a latecomer to this article but do I read you correctly that it is not possible to view the Attachments file to a List as I would view a Document Library?

    Thanks-

    Charlie Epes

  31. iyad says:

    it’s really great article..
    is it possible to add more file field to the form and make all the fields visible all the time

    • Iyad,

      This shouldn’t be a problem, though you may need some validation code to remove any unused ones if they’re empty. SharePoint might blow up on there being 5 fields but only 3 of them having data in them (as an example).

      You can look at the OKAttach function to see how SharePoint builds the fields, then just mimic something similar with jQuery to build the fields when the form loads.

      Mike

  32. @jelly says:

    Thank you for this post…I’m not a developer so any help would be great.
    I’m building a workflow (approval process) so that when the workflow is started an email will be sent to the proper compliance review person. We are trying to develop efficiencies so that all we have to do is start the workflow and not create a bunch of emails to each line of business. I have everything set up, the only issue is I cant figure out the “Add to body” selection to obtain a link directly to the “Current item” list attachment. I can create a link to the list item, but we want to cut out a step for the reviewer and just provide a link directly to the attachment.
    I can only get the email to populate up to /ID/… I need a way to populate Filename and extension within the email based on the current list item.

    If you have other options, let me know…I dont have access to the .Actions schema and I cant change too much because our enterprise is limiting access. Is there an out of the box/ or easy work around to accomplish the above?

    • @jelly,

      Unfortunately you may be stuck here. The attachments aren’t actually stored with the list item (they’re just displayed with the appropriate one). Remember that attachments are a one to many relationship meaning that you can have multiple attachments on a single list item. Without some code there’s no easy way to grab the list of attachments for a particular item (as far as I’m aware).

      One of the easiest ways is to call the attachments web service which will give you all of the attachments for the particular list GUID and record ID that you specify. There’s other ways to do it also, but not without code and I’m 99% sure that a SharePoint Designer workflow won’t do that for you. You’d need to custom build a workflow to accomplish that type of query.

  33. Gopalakrishnan says:

    Is there any way to download multiple attachments to a list item at once, programmatically, using SharePoint Object Model (MOSS)?

    • Absolutely! You can do it using the object model and deploy your code as a feature, or you can do it via web service. I previously worked with a client side application that pushed attachments to a list via web service to bulk load data. You will need to pass the web service the ID of the item you want to append the attachments to, and a binary string of the contents of the file.

  34. Rusty says:

    Is there a “blocked file type” for list attachments that is different from the blocked file types list in Central Admin? I am able to upload .cer files into document libraries but I am unable to attach .cer files to list items.

    I get an error when trying to attach: “This list item was saved but one or more attachments could not be saved. (”C:\filename.cer”) has been blocked from this website by the server administrators.

    • Rusty,

      I would think that the blocked file types list configured in Central Admin would be the list the list attachment method checks against. That said, I wonder if there’s a setting in IIS that prevents the uploading of a .cer file; that wouldn’t explain why it works in a document library though. I’m not sure it would be a best practice to store cert files on a list, but that’s another topic.

      • Rusty says:

        I figured it out .

        It appears that list item attachments look to the Central Admin web app for the blocked list. So, removing .cer from blocked file types for my intranet web app and for the Central Admin web app did the trick.

      • Rusty says:

        I was a little confused about this note in the blocked file types description

        NOTE: To allow a file type that is currently blocked, you must delete it from both the global and Web application lists.

        I thought maybe by global list they meant 12\CONFIG\docextflt, but maybe global means in the blocked list for the central admin web app.

        Looks like docextflt may be just a template for new web apps.

      • Glad you got it resolved, and thank you for sharing the resolution!

Trackbacks

Check out what others are saying about this post...
  1. [...] Kumar, Wael Abbas, and Edin Kapic, along with EUSP authors Michael Greene and Marc Anderson (individually, and in conversation) offer some other ideas for using the [...]




Notify me of comments to this article:


Speak and you will be heard.

We check comments hourly.
If you want a pic to show with your comment, go get a gravatar!