Tweet |
The component has a mandatory attribute named "sobjId" - this is the id of the sobject that the uploaded files will be attached to. Note that you can't use the name "id" for the attribute, as this is a standard attribute that allows the component to be identified by other components in the page.
The Attachment sobjects backing the input file elements are stored in a list in the controller that is initialised in the constructor to contain a single element. Clicking the Add More button causes another five Attachment sobjects to be appended to the list. As the user can choose to fill in the attachments in any order (e.g. just populating the last two elements), we can't simply insert the attachments when the save button is clicked, as that will attempt to insert the empty attachments that the user hasn't supplied details for. Thus we iterate the list and only insert those Attachments that have the Body field populated.
Note that there is no field for the user to input the name of the Attachment - I'm using the filename for the this, as the filename extension gives a browser a good chance of automatically opening the attachment when it is downloaded.
The Done button simply takes the user back to the view page for the object matching the supplied id attribute.
There's a couple of wrinkles when dealing with file uploads:
- When the page initially renders with one file chooser, if you choose a file and then click the Add More button, the chooser will not remember the file that you chose. This is browser behaviour, as it is not possible to prefill a file chooser under any circumstances - otherwise malicious pages could upload files from your hard disk without your consent.
-
The body of the uploaded file is stored as a Blob. To unit test code
that handles file uploading, you can create a Blob from a String using the
valueof method:
Blob bodyBlob=Blob.valueOf('Unit Test Attachment Body');
Note that when using the example page, you'll need to supply the id of an existing sobject (Account, Opportunity etc).
This is amazing!! Thank you! Just what I needed.
ReplyDeleteHey Bob, one thing you might try to avoid the filename clearing issues when adding more would be to render the page initially with a large number (say 100) apex:inputFile tags all wrapped with an output panel of with display:none;. Note that we couldn't use rendered here, because we need the file input tags to be present in the DOM. When the add more button is clicked used javascript to unhide 5 of the hidden input file panels.
ReplyDeleteHi Bob,
ReplyDeleteYour code was very helpful, Thanks for sharing.
My requirement is bit more complex, when user uploads multiple attachments from a visual force page i have to insert those attachments into multiple records that are related to that particular user.
Is there anyway i can achieve this.
As per your code I can insert multiple attachments into single record, but i have to insert those attachments into multiple records.
Did you get the solution to your issue I also have multiple record on the same vf page and have to attach user defined files to upload to each record.Please let me know.
DeleteYou can use this code as a starting point. You'll need to change the setting up of the parent id for the attachments, so that these point to the appropriate records. You'd also need to figure out which records the files get attached to - e.g. would these be determined via code or would the user be able to choose them in some way.
ReplyDeletehi Bob,
ReplyDeleteWhen the page is submitted, salesforce checks whether the files are too big (5MB+), or if the filenames are too long (80+ chars).
While it's not a problem getting these errors to display, how would you suggest we show which of the files generated any specific error message?
Say, if you have 5 files, two are OK, and one has a long file name, this code will upload two files that are OK, and will have an error for the third one, but will not say which file exactly had caused an error.
Also, to clarify the problem I listed above - Salesforce checks for all those errors before any controller code is executed, and adds some default error messages to ApexPages. How can we add say filenames to those messages?
ReplyDeleteOK, figured it out:
ReplyDelete1) Had to use a String variable for file name, and then assign it to Attachment.name. This gets around the 80 character limit that inputFile puts on file names.
2) Had to use a Blob variable for file body, and then assign it to Attachment.Body. This gets around SF checking the file size, and automatically ignoring files >5MB.
3) Once the form is submitted, you will have access to all file properties via the String and Blob variables you created, and can generate detailed error messages for the user, referencing file name etc. This becomes especially convenient when you have multiple files that are being uploaded, and only some of them cause errors, but the other ones get uploaded correctly.
Thanks for the post!
See this other post from Bob about how to handle this (wasn't possible in 2012 but has been since advent of HTML 5): http://bobbuzzard.blogspot.com/2014/09/check-file-size-on-upload-in-visualforce.html
DeleteThat's pretty much the pattern if the standard validation isn't giving you the info - bypassing it and validating yourself server side. Thanks for sharing your findings.
ReplyDeleteHow do I get the sObject Id? For example I want to attach this to a lead record, how do I do that?
ReplyDeletegood post, thanks to share it!
ReplyDeletewhat about the remove link? I'm trying to do it but I can't it works...any suggest or idea?
That should be fairly straightforward.
ReplyDeleteAdd an apex:commandlink to the table with a nested apex:param to pass the attachment record id to the controller - this will have to have a rerender attribute to work - I'd use the id of the entire table. There's more detail at this blog post: http://bobbuzzard.blogspot.com/2011/07/passing-parameters-to-apex-method-from.html
Tie this to an action method that deletes the attachment record based on the id and return null.
Thanks Bob this is very helpful. is it possible to rerender only the list of attachments after the attachment has been uploaded?
ReplyDeleteAs you can't rerender when using an inputfile component, I don't think you'll be able to do this.
ReplyDeleteHi Bob,
ReplyDeleteHow do I call the apex component to my VF page and how can I give sobjId to that of mine. What does objId refer to in here
sobjId is the Salesforce ID of the record that you are going to associate the attachments with. For example, if you want to upload attachments to a particular account, you would set the sobjId to the account id.
DeleteThanks for this post very useful. I am getting ** view state exceeded ** error when uploading files.
ReplyDeleteapex:inputFile accept="PDF, TXT" attribute is not honored in some browsers, so I have use ContentType controller. this is causing the viewstate exceeded error.
When I use this code as is I am not getting any error. any suggestions on how to get around this error?
@sfdummy - I ran into the same issue when branching Bob's code. The issue is that if you use the file chooser to select a file and then cause a page refresh without doing anything with the blob, it'll serialize the file itself into the viewstate and unless it's a really small file, this will throw you over the viewstate governor limit.
DeleteHere's a post that talks about it: http://developer.force.com/cookbook/recipe/uploading-a-document-using-visualforce-and-a-custom-controller
Here's the relevant note:
Make sure you take a look at the finally block in the controller code above. The finally block always executes when the try block exits regardless if an error occurs or not. You need to ensure you clear out document’s body (document.body = null) so that the blob is not automatically included in the serialized image of the controller. If you do not clear out the body, you’ll get the following view state error: "Maximum view state size limit (128K) exceeded. Actual viewstate size for this page was…"
Great post Bob! Kudos
ReplyDeleteHi can you send the code for Uploading Multiple Attachments via Visualforce
ReplyDeleteYou can download it from the link in the post.
Deletehi bob great job...I want how to upload multiple attachments into customobject
ReplyDeleteCustom objects are no different to standard objects with regard to attachments. The attachment records simply need the id of the custom object as their parentid.
Deletehi bob can u provide code for about screen shot my mail id:sreenathyadavcloud@gmail.com
ReplyDeleteThere's a link at the bottom of the post to the Visualforce, apex and unit tests.
DeleteHey Bob, thanks for sharing this. How we attach the screenshots to a Standard object record during create since we do not have the ParentID yet?
DeleteGreat tool! We were using a free one from the appexchange, but it's no longer supported by any browser, and was always sketchy on mobile devices.
ReplyDeleteThis one works great, especially since there's no java, flash or CSS.
Here are a few things I had to do for my org:
1.) add a line to the vf component under the button commads so my users wouldn't kill me for not warning them: NOTE: If you need to upload more than 6 files, click "Add More" button before you start browsing for files; the action reloads the page and doesn't remember what you've already selected.
2.) Separate the test class out, since in-line testing is no longer admitted. Don't forget to re-declare the static variable: public static final Integer NUM_ATTACHMENTS_TO_ADD=5;
3.) Create a button to use on our Opp page (URL): /apex/MultiAttachment?id={!Opportunity.Id}
Use the merge field picklist in the button creation page to replace opp id when adding to different objects.
4.) Immediately add 5 browse fields upon loading: add "addMore()" to the method called when loading.
public MultiAttachmentController()
{
// instantiate the list with a single attachment
newAttachments=new List{new Attachment()};
addMore();
}
If you do this, you'll need to update the test class:
System.assertEquals(1, controller.newAttachments.size());
needs to be
System.assertEquals(6, controller.newAttachments.size());
and
System.assertEquals(1 + NUM_ATTACHMENTS_TO_ADD, controller.newAttachments.size());
needs to be
System.assertEquals(6 + NUM_ATTACHMENTS_TO_ADD, controller.newAttachments.size());
Whoops, the formatting for the vf component note didn't take:
Deleteit needs to be a footer facet: apex:facet name="footer"
i want similar output on the contact detail page
ReplyDeletewhat should i do for it
what approach should i keep in my controller coding
Have you tried to do this for salesforce classic?
ReplyDeleteThank you very much for this code. One issue is that the previously selected values gets lost once a new row is added . Anyway around to retain the prev selected values ?
ReplyDeleteThis comment has been removed by the author.
ReplyDeleteThis is a very good post Bob, but my requirement is to add multiple attachments on an Send Email Page. Here the Task Record is not saved and does not have the taskId with which I need to associate the attachments. Is there an easy way to reuse your scripts or how else can I invoke the attachment picker which allows to associated multiple attachments from a Send Email VF Page.
ReplyDeleteThanks for this post! I always find your answers helpful when you comment on the issues I'm working on. If I google and I see your name on the results, I know I'm going to get an answer that works for me.
ReplyDeleteAwesome.. Works like a charm.. Thanks a lot for ur post and for sharing ur knowledge.
ReplyDeleteAwesome.. Works like a charm.. Thanks a lot for ur post and for sharing ur knowledge.
ReplyDeleteSo Bob, forgive me, but I'm a newbie. If I want to attach the files to an account (integrating this VF page into the account detail page), how do I jigger the apex attribute description in the controller? Thanks!
ReplyDeleteHI , Where can i see the code. The functionality is really helpful.
ReplyDeleteHi Bob , I hav eone question is there any way that we can prevent page refresh becuase we lost all attachment once we click on Add More. Also , i know do not work with rerender so can you suggest me any workaround ?
ReplyDeleteHi Bob,
ReplyDeleteCan't we get the file choosen already when we click on add more?
Hi bob,
ReplyDeleteAwsome work. One issue is that the previously selected values gets lost once a new row is added . Anyway around to retain the prev selected values ?
Hi Bob,
ReplyDeleteThis works perfectly. However my whole page refreshes when I click "add more". Any idea how to solve this?