function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
ShanghaiDrewShanghaiDrew 

Trigger to Add Content when a new Attachment is Inserted

I am attempting to design a trigger to insert new Content (ContentVersion) when a new attachment is added.  We are switching from using attachments to Content.  However, the majority of files are added through via the desktop email programs (using Maildrop and Salesforce for Outlook) so I have written a trigger to copy the file to Content. Unfortunately, I can't seem to get it working for the body/file.  After insert, the attachment body referenced seems to be in some temp location and before insert the attachment has yet to be loaded so it can't find any body.

 

When I use before insert, I get the following error: Error: Required fields are missing: [Body]

 

When I use after insert, I get the following error: Error: Apex trigger NewAttachment caused an unexpected exception, contact your administrator: NewAttachment: execution of AfterInsert caused by: System.DmlException: Insert failed. First exception on row 0; first error: UNKNOWN_EXCEPTION, java.io.FileNotFoundException: /tmp/bvf/cmp_5879646311450005283.def (No such file or directory): []: Trigger.NewAttachment: line 56, column 5

 

Any suggestions?

 

Here's my trigger:

 

trigger NewAttachment on Attachment (before insert) {

List<Attachment_Tracker__c> tracker = new List<Attachment_Tracker__c>();
List<ContentVersion> CV = new List<ContentVersion>();

for (Integer i = 0; i < Trigger.new.size(); i++) {
	String AttParent = Trigger.new[i].ParentId;
	String ContentDesc = '';
	String FileName = Trigger.new[i].Name;
	Blob BodyData = Trigger.new[i].Body;
		// Check to make sure there is a ParentID for the attachment and if records already exist for this Parent
		if(AttParent!=null) {
			String AttParSub = AttParent.substring(0,15);
			Map<String,ID> TrackMap = new Map<String,ID>();
			for (Attachment_Tracker__c c: [select Contact__c, id from Attachment_Tracker__c Where Contact__c =:AttParent] ) {
				TrackMap.put(c.Contact__c, c.id);
			}

			// Make sure the ParentID is a ContactID and it isn't already in the Attachment Tracker
			if(AttParent.startsWith('003')) {
				//Get the Contact Name
				Contact FullName = [SELECT Name, AccountID, Title FROM Contact WHERE Id = :AttParent];
				ContentDesc = FullName.Name+' ('+FullName.Title+' at '+FullName.AccountID+')';
				String ContName = FullName.Name;
				String ContAcct = FullName.AccountID;
				String ContTit = FullName.Title;
				// Get ID to set the Workspace to Resumes
				ContentWorkspace Workspace = [select id from ContentWorkspace where name = 'Resumes' limit 1];

				////////////////////////////////////////
				// First Copy Attachemnet to Content //
				//////////////////////////////////////
			    CV.add(new ContentVersion(
			    	FirstPublishLocationId = Workspace.id, 
			    	// Title = Trigger.new[i].Name,
			    	Contact__c = Trigger.new[i].ParentId,
			    	PathOnClient = Trigger.new[i].Name, 
			    	VersionData = BodyData
			    	) );

				////////////////////////////////////////
				// Now Update the Attachment Tracker //
				//////////////////////////////////////	
				if(!TrackMap.containsKey(AttParent)){
					tracker.add(new Attachment_Tracker__c(
		            Contact__c = Trigger.new[i].ParentId,
					File_Name__c = Trigger.new[i].Name,
					ContactName__c = ContName,
					Job_Title__c = ContTit,
					Company__c=ContAcct
	                ) ) ;
				}
	        }
		}
    insert tracker;
	insert CV;
	}
}
Yuta ShimizuYuta Shimizu

I solved this problem.

I want to know why this code works...

 

trigger ForwardCaseEmail on Attachment (after insert) {
for(Attatchment att : trigger.new){
Attachment att_fromdb = [select id, Name, body from Attachment where id = :att.id]; ContentVersion cv = new ContentVersion();
cv.Title = att_fromdb.Name; cv.PathOnClient = att_fromdb.Name; cv.VersionData = att_fromdb.body; cv.RecordTypeId = 'your_record_type_id';
cv.FirstPublishLocationId = 'your_workspace_id';
insert cv; }
}



 

Here-n-nowHere-n-now

I used the same code like Yuta's, except for moving the SOQL out of the loop, but still got the same FileNotFoundException for files bigger than just a few tens of kBs (haven't determined the precise size limit).  It's just strange why the size would matter.  I've opened a case with SFDC.

Douglas C. AyersDouglas C. Ayers
For folks interested in migrating to Chatter Files then there may be lots of classic Attachments that need to be migrated over.

There are a few items to consider, such as does the parent of the attachment even support chatter files? What if the attachment is private?

I've written a blog post and posted my code on github that provides options for handling the conversion of Attachments to Chatter Files and Notes to Content Notes. Hope this helps!

https://douglascayers.com/2015/10/10/salesforce-convert-attachments-to-chatter-files/

https://github.com/DouglasCAyers/sfdc-convert-attachments-to-chatter-files
Snehil KarnSnehil Karn
Hi All,

I used the following code to convert Case Attchment to Case chatter file (Content Version):
 
Attachment at = [Select Body, Id, Name, OwnerId,ParentId From Attachment LIMIT 1]; 
ContentVersion cv = new ContentVersion(); 
cv.ContentLocation = 'S'; 
cv.PathOnClient = at.Name; 
cv.Origin = 'H'; 
cv.OwnerId = at.OwnerId; 
cv.Title = at.Name; 
cv.VersionData = at.Body; 

insert cv; 

ContentVersion cv = [select ContentDocumentId from ContentVersion where Id =: cv.Id]; 

ContentDocumentLink cl = new ContentDocumentLink(LinkedEntityId = at.ParentId, ContentDocumentId = cv.ContentDocumentId, ShareType = 'I'); 

insert cl;


This sample code convers an Attachment record to a Chatter File. You can later delete the attachment if you do not need it.
Jean Mendez 3Jean Mendez 3
Hi Snehil Karn,

Did you used this code inside a trigger?. 

Thanks