Hi Everyone,
In this post, I am going to show you how to download and Preview the files from Lightning Community using Lightning Web Component.
Before we get started, let’s discuss the Object Structure of the files to know how the files get stored into Salesforce.
Content Document: – Represents a document that has been uploaded to a library in Salesforce CRM Content or Salesforce Files.
Content Version: – Represents a specific version of a document in Salesforce CRM Content or Salesforce Files. One Content Document can have multiple content versions.
Content Document Link:- Represents the link between a Salesforce CRM Content document or Salesforce file and where it’s shared. A file can be shared with other users, groups, records, and Salesforce CRM Content libraries.
Here is the complete diagram.
This is how your component will look like once you have completed the development of the component.
Features
- Preview the Files in both Lightning Experience and Lighting Community
- Download the Files in both Lightning Experience and Lighting Community
- Delete the files
- Upload new files under the same record
- Filter the files based on created date and file title
- Sync the files from Salesforce if the file has been uploaded from a different place.
- Control what information needs to show in the table.
Limitations
- The only limitation of the component is, we can use it under the record detail page only.
Use Component inside Salesforce Record Page in Lightning Experience
To use the component, Open the record detail page of any object where you wanted to use this component. Click on gear icon and then click on Edit page.
In the left, search for content manager, drag and drop the component where you wanted to place.
It requires some additional information that your salesforce admin can setup. See below screenshot
- Title: – Title of the Lighting Card
- Record Id: – use recordId as value for Lighting Experience and {!recordId} if component is used in Lightning Community
- Do you want to show Details? – to display the file details. Keep as true
- Accepted file format to upload the files? – It has some default values. if you wanted to remove or add make the adjustment.
- User Can Sync the files from Salesforce? – If true it will show a button to sync the files from salesforce.
- Do you want the users to upload the new files? – If true, the user will be able to see the upload button.
- The component is used in the community? – Check this as true if the component is being used in the Salesforce Community.
- Show Filters? – If true, the user will be able to see the filters.
Let’s setup the code.
Complete Code
You can get the complete code from Here.
Create the Apex Class.
/**
* @description :
* @author : Amit Singh
* @group :
* @last modified on : 12-02-2020
* @last modified by : Amit Singh
* Modifications Log
* Ver Date Author Modification
* 1.0 11-26-2020 Amit Singh Initial Version
**/
public with sharing class ContentManagerService {
@AuraEnabled
public static String getContentDetails(String recordId) {
List<ContentDocumentLink> contentDocumentList = [SELECT ContentDocumentId, LinkedEntityId
FROM ContentDocumentLink
WHERE LinkedEntityId =: recordId];
Set<Id> contentDocumentId = new Set<Id>();
for(ContentDocumentLink cdl : contentDocumentList){
contentDocumentId.add(cdl.ContentDocumentId);
}
List<ContentVersion> contentVersionList = [SELECT Id, VersionData, FileType, Title, FileExtension,
ContentDocument.CreatedBy.Name, ContentDocument.ContentSize,
CreatedDate, ContentDocumentId, ContentDocument.FileType
FROM ContentVersion
WHERE ContentDocumentId IN : contentDocumentId];
return JSON.serialize(contentVersionList);
}
@AuraEnabled
public static void deleteContentDocument(String recordId) {
Database.delete(recordId);
}
public static string ContentType(String fileType) {
switch on fileType.toLowerCase(){
when 'docx' {
return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
}
when 'csv' {
return 'application/vnd.ms-excel';
}
when 'wav' {
return 'audio/wav';
}
when 'wmv' {
return 'video/x-ms-wmv';
}
when 'mp3' {
return 'audio/mpeg';
}
when 'mp4' {
return 'video/mp4';
}
when 'png' {
return 'image/png';
}
when 'pdf' {
return 'application/pdf';
}
when else {
return 'image/jpeg';
}
}
}
}
Create the Lightning Web Component and use below code.
Code for HTML file
<!--
@description :
@author : Amit Singh
@group :
@last modified on : 12-02-2020
@last modified by : Amit Singh
Modifications Log
Ver Date Author Modification
1.0 11-26-2020 Amit Singh Initial Version
-->
<template>
<lightning-card variant="Narrow" title={title} icon-name="standard:document">
<template if:true={isLoading}>
<lightning-spinner alternative-text="Loading" size="small" variant="brand"></lightning-spinner>
</template>
<lightning-button if:true={showsync} variant="brand" title="Content Sync" icon-name="utility:sync"
onclick={handleSync} slot="actions" >
</lightning-button>
<div class="slds-var-p-around_small" >
<div class="slds-grid slds-wrap">
<div class="slds-col slds-size_1-of-4" if:true={showFilters}>
<lightning-input if:true={showFilters} type="search" variant="standard" name="Title" label="Title" onchange={handleSearch} >
</lightning-input>
</div>
<div class="slds-col slds-size_1-of-4" if:true={showFilters}>
<lightning-input if:true={showFilters} type="search" variant="standard" name="Created By" label="Created By" onchange={handleSearch}>\
</lightning-input>
</div>
<div class="slds-col slds-size_2-of-4 slds-var-p-left_small">
<lightning-file-upload if:true={showFileUpload}
label="Upload New File"
name="fileUploader"
accept={accept}
record-id={recordId}
onuploadfinished={handleUploadFinished}
multiple>
</lightning-file-upload>
</div>
</div>
</div>
<div class="slds-var-p-around_small">
<lightning-datatable
key-field="id"
data={dataList}
hide-checkbox-column
columns={columnsList}
onrowaction={handleRowAction}>
</lightning-datatable>
</div>
</lightning-card>
</template>
Code for javascript files.
import { api, LightningElement, track, wire } from 'lwc';
import getContentDetails from '@salesforce/apex/ContentManagerService.getContentDetails';
import deleteContentDocument from '@salesforce/apex/ContentManagerService.deleteContentDocument';
import { NavigationMixin } from 'lightning/navigation';
const columns = [
{ label: 'Title', fieldName: 'Title', wrapText : true,
cellAttributes: {
iconName: { fieldName: 'icon' }, iconPosition: 'left'
}
},
{ label: 'Created By', fieldName: 'CREATED_BY',
cellAttributes: {
iconName: 'standard:user', iconPosition: 'left'
}
},
{ label: 'File Size', fieldName: 'Size' },
{ label: 'Preview', type: 'button', typeAttributes: {
label: 'Preview', name: 'Preview', variant: 'brand-outline',
iconName: 'utility:preview', iconPosition: 'right'
}
},
{ label: 'Download', type: 'button', typeAttributes: {
label: 'Download', name: 'Download', variant: 'brand', iconName: 'action:download',
iconPosition: 'right'
}
},
{ label: 'Delete', type: 'button', typeAttributes: {
label: 'Delete', name: 'Delete', variant: 'destructive',iconName: 'standard:record_delete',
iconPosition: 'right'
}
}
];
export default class ContentManager extends NavigationMixin(LightningElement) {
@api title;
@api showDetails;
@api showFileUpload;
@api showsync;
@api recordId;
@api usedInCommunity;
@api showFilters;
@api accept = '.csv,.doc,.xsl,.pdf,.png,.jpg,.jpeg,.docx,.doc';
@track dataList;
@track columnsList = columns;
isLoading = false;
wiredFilesResult;
connectedCallback() {
this.handleSync();
}
getBaseUrl(){
let baseUrl = 'https://'+location.host+'/';
return baseUrl;
}
handleRowAction(event){
const actionName = event.detail.action.name;
const row = event.detail.row;
switch (actionName) {
case 'Preview':
this.previewFile(row);
break;
case 'Download':
this.downloadFile(row);
break;
case 'Delete':
this.handleDeleteFiles(row);
break;
default:
}
}
previewFile(file){
if(!this.usedInCommunity){
this[NavigationMixin.Navigate]({
type: 'standard__namedPage',
attributes: {
pageName: 'filePreview'
},
state : {
selectedRecordId: file.ContentDocumentId
}
});
} else if(this.usedInCommunity){
this[NavigationMixin.Navigate]({
type: 'standard__webPage',
attributes: {
url: file.fileUrl
}
}, false );
}
}
downloadFile(file){
this[NavigationMixin.Navigate]({
type: 'standard__webPage',
attributes: {
url: file.downloadUrl
}
}, false
);
}
handleDeleteFiles(row){
this.isLoading = true;
deleteContentDocument({
recordId : row.ContentDocumentId
})
.then(result => {
this.dataList = this.dataList.filter(item => {
return item.ContentDocumentId !== row.ContentDocumentId ;
});
})
.catch(error => {
console.error('**** error **** \n ',error)
})
.finally(()=>{
this.isLoading = false;
});
}
handleSync(){
let imageExtensions = ['png','jpg','gif'];
let supportedIconExtensions = ['ai','attachment','audio','box_notes','csv','eps','excel','exe',
'flash','folder','gdoc','gdocs','gform','gpres','gsheet','html','image','keynote','library_folder',
'link','mp4','overlay','pack','pages','pdf','ppt','psd','quip_doc','quip_sheet','quip_slide',
'rtf','slide','stypi','txt','unknown','video','visio','webex','word','xml','zip'];
this.isLoading = true;
getContentDetails({
recordId : this.recordId
})
.then(result => {
let parsedData = JSON.parse(result);
let stringifiedData = JSON.stringify(parsedData);
let finalData = JSON.parse(stringifiedData);
let baseUrl = this.getBaseUrl();
finalData.forEach(file => {
file.downloadUrl = baseUrl+'sfc/servlet.shepherd/document/download/'+file.ContentDocumentId;
file.fileUrl = baseUrl+'sfc/servlet.shepherd/version/renditionDownload?rendition=THUMB720BY480&versionId='+file.Id;
file.CREATED_BY = file.ContentDocument.CreatedBy.Name;
file.Size = this.formatBytes(file.ContentDocument.ContentSize, 2);
let fileType = file.ContentDocument.FileType.toLowerCase();
if(imageExtensions.includes(fileType)){
file.icon = 'doctype:image';
}else{
if(supportedIconExtensions.includes(fileType)){
file.icon = 'doctype:' + fileType;
}
}
});
this.dataList = finalData;
})
.catch(error => {
console.error('**** error **** \n ',error)
})
.finally(()=>{
this.isLoading = false;
});
}
handleUploadFinished(){
this.handleSync();
//eval("$A.get('e.force:refreshView').fire();");
}
formatBytes(bytes,decimals) {
if(bytes == 0) return '0 Bytes';
var k = 1024,
dm = decimals || 2,
sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
handleSearch(event){
let value = event.target.value;
let name = event.target.name;
if( name === 'Title' ){
this.dataList = this.dataList.filter( file => {
return file.Title.toLowerCase().includes(value.toLowerCase());
});
} else if( name === 'Created By' ){
this.dataList = this.dataList.filter( file => {
return file.CREATED_BY.toLowerCase().includes(value.toLowerCase());
});
}
}
}
Code for meta.xml file
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>50.0</apiVersion>
<isExposed>true</isExposed>
<masterLabel>Content Manager</masterLabel>
<targets>
<target>lightning__RecordPage</target>
<target>lightning__UtilityBar</target>
<target>lightningCommunity__Page</target>
<target>lightningCommunity__Default</target>
</targets>
<!-- Configuring the design attributes -->
<targetConfigs>
<targetConfig targets="lightning__RecordPage, lightningCommunity__Default">
<property name="title" type="String" label="Title" default="Content Manager" required="true"/>
<property name="recordId" type="String" default="recordId" label="Record Id" required="true"/>
<property name="showDetails" type="Boolean" default="true" label="Do you want to Show Details ?"/>
<property name="accept" type="String" default=".csv,.doc,.xsl,.pdf,.png,.jpg,.jpeg,.docx,.doc"
label="User can upload the files in format?" />
<property name="showsync" type="Boolean" default="true" label="User can sync the files from Salesforce?" />
<property name="showFileUpload" type="Boolean" default="true" label="Do you want the users to upload a new file ?"/>
<property name="usedInCommunity" type="Boolean" default="false" label="Component is used in Community?" />
<property name="showFilters" type="Boolean" default="true" label="Show Filters?" />
</targetConfig>
</targetConfigs>
</LightningComponentBundle>
Deploy the code to your salesforce org and Enjoy.
Thanks for reading 🙂
#HappyReading #DeveloperGeeks #AskPanther #SFDCPanther
[…] https://testwebgrantha.xyz/sfdc/how-to-preview-files-in-lightning-community-using-lwc/ […]
[…] https://testwebgrantha.xyz/sfdc/how-to-preview-files-in-lightning-community-using-lwc/ […]
Hello Panther, is there any possibility we can developer same UI, based on External links(Video,Image,png) using LWC or Lightning. the feature need to have like zoom in/out, option also?
I did not get you. Are you talking about that the files are stored into external System.
How to access the storage in the external system? Only preview and download
If the file is stored in another server then you have to go with some other solution which might be a custom development or some solution available over appexchange.
[…] If you do not know about how the Files are stored in Salesforce, Please visit this Article. […]
Hello sir,
How to create new folder(with folder icon) using apex class in lightning web component.
When clicking foder icon then their input field folder name when enter name of folder then cliking on save button then save in database.
Hi,
When do you say folder are you referring to Document Folder? I believe you might need to use Metadata API.
Thank you! Save me a bunch of development time.
I had to tweak it a little (add a parameter to xml to adapt baseUrl) to handle preview&download for Community: it didn’t properly work in our multiple-community org. Would you happen to know a more robust way to download files rather than the semi-hardcoded url used here?
Hi,
I have tested this for many communities at least 4-5 and it’s working.
That is a bit odd but, for us, the {location.host}/sfc/servlet.shepherd/document/download/{ContentDocumentId} results in a community error and I had to add a in-between parameter to get {location.host}/{communityName}/sfc/servlet.shepherd/document/download/{ContentDocumentId}.
No big deal anyway, everything else is perfect.
Does work in communities for me either, Alexis were you able get it work?
Sorry, I missed your reply.
We did make it work by adding the community name inside the url (see my previous reply).
Basically we changed the JS function getBaseUrl() to get a different url if the component is in a Community or in basic Salesforce (variable this.communityName is passed through a parameter in xml)
getBaseUrl(){
let baseUrl = ""
if (this.usedInCommunity){
baseUrl = 'https://'+location.host+'/'+this.communityName+'/';
} else {
baseUrl = 'https://'+location.host+'/';
}
return baseUrl;
}
Yes, that’s the correct way to resolve this.
If you configure your component to target Experience Builder pages only then you can use the module @salesforce/community/basePath. See https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.reference_salesforce_modules
If you configure your component to target Experience Builder pages only you can use the module @salesforce/community/basePath. See https://developer.salesforce.com/docs/component-library/documentation/en/lwc/lwc.reference_salesforce_modules
Hi Alexis, I am not able to get this to work on my community site. Do you mind sharing your code so I can have a better look and understanding?
I have all the components and pressing the button I do get the content document id, but I am not able to preview/download the file.
[…] Read more […]
Hi, i have troubles with the visibility of Files.
I only see files i own. Also as Administrator.
I think that because of the visibility flag on the ContentDocument Object.
How did you solve this problem?
As a service employee i want to see all Files on a Case not only the files i uploaded.
BTW. Really nice piece of code 😉 you saved me a lot of time 😀
Could you please elaborate more on this so that I can provide you the solution.
We use the Service Cloud
ORG-Edition is Enterprise Edition
See these screenshots
Here no results are shown because the attachments are not mine
http://s000.tinyupload.com/download.php?file_id=83512678244536964000&t=8351267824453696400002834
Here only one attachment is shown because this was uploaded by me
http://s000.tinyupload.com/download.php?file_id=49165811798425021539&t=4916581179842502153940774
Hi, This is the expected behavior as per the code. If you have access to the files then you should be able to see the files.
I am Java Developer. Since i am new for sales force . My concept similar to this. That is how to display doc/docx file into UI components? using java/jsp . Can you explain how it possible using java ?
Hi Karthika,
I do not think you can do this using Java inside Salesforce. But yes if you are developing the Java Application then you can show the same using JSP. And I do not have idea about JSP.
This opens the file in a new tab. Not in a modal popup.
Yes it does because community does not support preview
If external URL is stored in custom field then can we preview and download file on same screen?
No Idea.You can give it a try
Amazing code! Thank you for sharing this. Everything appears to be working (uploading files are showing in the Files related list) but they are not showing in the grid for me. I am the admin and have this in our Sandbox with me as the owner of the record and the file. Any ideas on why this might be happening for me?
When you say not appearing in the Grid are you referring to DataTable?
Yes. The file uploads to the record but does not show in the DataTable.
Event after refreshing it’s not showing?
Correct
Strange. Are you getting any errors in the console.log?
No errors in the console log.
Actually my bad! I am getting an error in the console log.
**** error ****
h {status: 500, body: {…}, headers: {…}}
body:
exceptionType: “System.LimitException”
isUserDefinedException: false
message: “JSON string exceeds heap size limit”
stackTrace: “Class.System.JSON.serialize: line 3, column 1\nClass
There is a limit of 6MB if your files are greater than that then you will get the error.
To resolve the error Remove “VersionData” From the below Query and it will work
List contentVersionList = [SELECT Id, VersionData, FileType, Title, FileExtension,
ContentDocument.CreatedBy.Name, ContentDocument.ContentSize,
CreatedDate, ContentDocumentId, ContentDocument.FileType
FROM ContentVersion
WHERE ContentDocumentId IN : contentDocumentId];
File preview in not working in community using the above solution. could you please help?
What is the issue that you are facing?
When I click on the file name, this url is opened in a new window (/sfsites/c/sfc/servlet.shepherd/version/renditionDownload?rendition=THUMB720BY480&versionId=’ + this.file.Id)
But it just shows first page as thumbnail, no download link or view file details.
I have updated the code. Can you check now and see if this works for you?
Sorry saw the updated code. So you basically are trying to add community name to the URL right? It still does not work.
I have tried this for more than 6+ Different experiences and it is working for me even the previous code is also working for me.
File preview solution doesnt work in communities..it redirects to a community page but displays invalid page.
I have updated the code. Can you check now and see if this works for you?
Is that possible to work with Cases and files related to Cases? I mean, Files related to a Case.
Yes, this component is for all the objects where you can upload files.
Thanks for this. Any ideas on how to improve the Apex code to overcome a heap size limit
If you are querying the file and their content it’s hard to improve there are ways like using content libraries. or you can use some javascript libraries for the same. I have not used any of there so not able to provide more insight on this.
How to show a pdf file? Can you please elaborate. I want to show all the pages of the pdf.
The same code will display the pdf file as well but if the pdf size is more then it will not display any pages instead it will display the download button.
Hi , I am unable too see the file as guest user . Image is breaking whereas I can see with logged in user . I have enabled the salesforce CRM content for guest user and the object where file is attached is also public read-only for now .Can you please help .
So far I know, to display the file to the guest user you must need to make sure your file is visible by guest user using some trigger on content version or using content distribution user.
Due to security issues the guest user can not see the images
how to add pagination to this data table?
I have shared a blog for the pagination. You can refer that blog – https://testwebgrantha.xyz/sfdc/how-to-implement-pagination-in-lightning-web-component/
I want this for salesforce mobile app. Is it possible?
Datables are not supported in Mobile applications. You need to tweak the code a bit and then it will work but you have to make the changes according to yourself
The preview does not work properly in LWR sites atleast. Only shows the first page of the document although the document having multiple pages.
Yes that is true
Hi, I have used this code and its working fine, but unfortunatly the document in the preview or download cloumn does not fit in to the title in the same row. It is mixed up between the rows.
Do you have any ideas what is the problem?
The problem is because of the space for the table. Either have a free space or move the buttons to the dropdown using row level actions.
Thanks for the quick reply. Im not sure if I get you right. Where do you think free space is missing?
The table is like this:
Titel | Caption | Section | Preview | Download
Pic1.png | Cap1| Sec1| Preview_Button1 | Download_Button1
Pic1.png | Cap1| Sec1| Preview_Button1 | Download_Button2
Now Preview_Button2 points to Pic1.png and Preview_Button1 to Pic2.png:
Screenshot of table in GUI:
https://ibb.co/3phL3qY
Thank you!
From Your attached screenshot I do not see any problem. However, instead of showing the button as a table column, you can show those as drop-down next to each row. Refed the below link
https://developer.salesforce.com/docs/component-library/bundle/lightning:datatable/example#lightningcomponentdemo:exampleDatatableStaticRowActions
Hi.. Is there any fix for PDF files showing only the first page in Preview mode.?
I am looking into this.
What if the file is stored on another server?
If the file is stored in another server then you have to go with some other solution which might be a custom development or some solution available over appexchange.
Hello,
We would like to be able to transfer file (copy) from Content manager row to another Case. How it is better to to so? I tried use screen Flow but doesn’t work.
Could you help me please?
What is your use case? I did not get it
Hi,
How would i apply custom css to the table column names and different custom css to table rows in this code ?
You have to do some CSS hacks using the SLDS classes. There is no direct way you have to do hit and try
Hi,
I have the similar requirement, but I want the preview feature in an Omnistudio/Vlocity Omniscript page. Is it possible or is there any workaround ?
Thanks in advance.
I have no idea about velocity
Hi, Amit actually I m trying to get ContentDocumentId but its always say undefined in lwc datatable
How you are getting the Id?