3

I'm working on a webservice to create new Salesforce Files. To validate the underlying methods, I manually uploaded a PNG file, which shows up in the file preview in Files.

I can retrieve the ContentVersion record via SOQL and create a new File, which does open correctly as an image if I download it and specify the application, but the image does not appear in the file preview. FileType is determined based on ContentURL and/or(?) PathOnClient, but these require the ContentVersion.Id, which doesn't exist until after the file is created, and cannot be edited.

Given a PNG with id 'originalImageId', I can retrieve attributes using the following:

String myId = 'originalImageId';
ContentVersion cv = 
    [SELECT Title, VersionData, ContentDocumentId, 
            ContentLocation, FileType  
     FROM ContentVersion 
     WHERE ContentDocumentId = :myId 
     AND IsLatest = true];

String encodedBlob = EncodingUtil.base64Encode(cv.VersionData);
String newId = FileManager.createFile(cv.Title, encodedBlob, cv.FileType);

which calls

public class FileManager {

  public static Id createFile(String fileName, String base64Data,
                             String fileType, Id contentDocumentId){

    base64Data  = EncodingUtil.urlDecode(base64Data, 'UTF-8')                                 ;

    ContentVersion cv = new ContentVersion();
    cv.ContentLocation = 'S'; // 'S' = a Salesforce File

    cv.ContentDocumentId = contentDocumentId;        

    cv.VersionData = EncodingUtil.base64Decode(base64Data);
    cv.Title = fileName;
    // path will be different for Production
    String path = 'https://nxstage--qa--c.cs92.content.force.com/sfc/servlet.shepherd/version/download';                             
    cv.PathOnClient = path;

    Utils.DatabaseInsert(cv);

    Utils.debug('The contentDocumentId for ' + fileName + ' is ' + cv.ContentDocumentId);

    return cv.Id;
 }

 public static Id createFile(String fileName, String base64Data,
                             String fileType){

    String newId = createFile(fileName, base64Data, fileType, null);
    Utils.debug('The ContentVersion.Id of ' + fileName + ' is ' + newId);

    return newId;
 }

Is there a trick to setting PathOnClient at create time / on insert that will allow me to specify the filetype?

  • ContentVersion.PathOnClient docs (developer.salesforce.com/docs/atlas.en-us.object_reference.meta/…) say, Specify a complete path including the path extension in order for the document to be visible in the Preview tab. So it looks like it's based on file extension? Try appending Title and FileExtension from the original CV to your PathOnClient and see if that works. Commenting since this is untested guesswork, though. – Thomas Taylor Apr 8 at 14:40
  • I did some experimentation with this, and was unable to generate a ContentVersion that had a preview. But, setting PathOnClient to Title + '.' + FileExtension does get correct values for FileExtension and FileType (ie, they match the original file). The string that you're using for PathOnClient results in a FileExtension of shepherd/version/download (it looks for the last ., I guess) and a FileType of UNKNOWN. In the file list, there is a filetype icon indicating that the new file is an image, but no preview. – Thomas Taylor Apr 9 at 14:24
3

OK, I got this to work. The key to getting the FileType set correctly and thus a preview generated is to ensure that the proper file extension is set via ContentVersion.PathOnClient.

Additionally, while it may be necessary in your final web service, the base64Data = EncodingUtil.urlDecode(base64Data, 'UTF-8') creates a bad version of the file data when used via anon apex as in your example. I figured this out because when I downloaded the preview-less files, they wouldn't open as images.

I changed the signatures, queries and calls to include ContentVersion.FileExtension, and then set PathOnClient to be Title + '.' + FileExtension as I said in my comment above.

The resulting FileManager class is then:

public class FileManager {

  public static Id createFile(String fileName, String base64Data,
                             String fileType, String fileExt, Id contentDocumentId){

    //base64Data  = EncodingUtil.urlDecode(base64Data, 'UTF-8')                                 ;

    ContentVersion cv = new ContentVersion();
    cv.ContentLocation = 'S'; // 'S' = a Salesforce File

    cv.ContentDocumentId = contentDocumentId;        

    cv.VersionData = EncodingUtil.base64Decode(base64Data);
    cv.Title = fileName;
    cv.PathOnClient = fileName + '.' + fileExt;

    //Utils.DatabaseInsert(cv);
    insert cv;

    System.debug('The contentDocumentId for ' + fileName + ' is ' + cv.ContentDocumentId);

    return cv.Id;
 }

 public static Id createFile(String fileName, String base64Data,
                             String fileType, String fileExt){

    String newId = createFile(fileName, base64Data, fileType, fileExt, null);
    System.debug('The ContentVersion.Id of ' + fileName + ' is ' + newId);

    return newId;
 }
}

And the anonymous block:

String myId = '069nnnnnnnnnnnnnnn';
ContentVersion cv = 
    [SELECT Title, FileExtension, VersionData, ContentDocumentId, 
            ContentLocation, FileType  
     FROM ContentVersion 
     WHERE ContentDocumentId = :myId 
     AND IsLatest = true];

String encodedBlob = EncodingUtil.base64Encode(cv.VersionData);
String newId = FileManager.createFile(cv.Title, encodedBlob, cv.FileType, 
    cv.FileExtension);
  • 1
    Thanks for all your work, Thomas! I'll give it another go this afternoon (ever the optimist) and post an update. – Duncan Stewart Apr 9 at 15:13
  • 1
    Brilliant!! Works like a charm :D – Duncan Stewart Apr 9 at 18:19

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.