[SalesForce] Save/update a value to static resource apex

I want to save new image or update the already existing file at a static resource. And then this image file will be used to be rendered in pdf. Can anyone provide me a better way to do this as it says that we can't perform DML operations on static resources.

In the VF page i'm building a chart from a list data and saving this on the canvas and converting it to the image and save it to the attachment.

Apex Code

@RemoteAction
global static void saveChartAsAttachment(String prjId,String attachmentBody){
 //  system.debug('@@@saveChartAsAttachment  oppId ='+ oppId);
 system.debug('Project ID: '+ prjId);
   List<Attachment> listAttachment =[Select Name,Id,Body from Attachment Where ParentId=:prjId];
   system.debug('Size of Attachment: '+listAttachment.size());
   Attachment att;
   if(listAttachment.size()>0){
      att =listAttachment[0];
      att.Body = EncodingUtil.base64Decode(attachmentBody);
      update att;
   }
   else{

       att = new Attachment();
       att.Name='spiderChart_VF';
       att.ParentId=prjId;
       att.ContentType='image/png';
       att.Body = EncodingUtil.base64Decode(attachmentBody);
       insert att;

   }

VF Page Code

        <script>
   function savePNG(){

            var mainDiv=document.getElementById('chart_div');
            var svg = mainDiv.children[0].children[0].innerHTML;
            console.log('@@svg .... ' + svg);
            canvg(document.getElementById('canvas'),svg);
            var img = canvas.toDataURL("image/png"); //img is data:image/png;base64
            img = img.replace('data:image/png;base64,', '');
            var data = "bin_data=" + img;
            alert(data);
            alert(img);

            Visualforce.remoting.Manager.invokeAction('{!$RemoteAction.VWSizingReport02Controller.saveChartAsAttachment}',
           '{!Proj.ID}',img,
              function(result,event){
                 if(event.status){
                 alert("DOne");
                   console.log('@@@ success');

                 }
                 else {
                   console.log('@@@ fail');
                 alert("Error");
                 }


              } );


        }
    </script>
<input type="button" id="save_img_att" value="Save as Attachment" onclick="savePNG();"/>   
            <div id="chart_div">
               <p class="valve-notes" >
                    <label class="valve-notes">Flow Curve Graph:</label>
                </p>

                <apex:chart animate="false" resizable="true" height="480" width="680" data="{!chartData}">
                    <apex:legend position="bottom"/>
                    <apex:axis steps="10" type="Numeric" position="left" fields="percentCv" 
                               title="% Cv"/>        
                    <apex:axis steps="5" maximum="100" type="Numeric" position="bottom" fields="percentOpen" 
                               title="% Open"/>
                    <apex:lineSeries title="Maximum Flow" axis="left"  xField="maximumFlow" yField="percentCv"
                                     markerType="circle" markerSize="2" markerFill="#FF0000"/>
                    <apex:lineSeries title="Normal Flow" axis="left"  xField="normalFlow" yField="percentCv"
                                     markerType="circle" markerSize="2" markerFill="#17ff00"/>
                    <apex:lineSeries title="Minimum Flow" axis="left"  xField="minimumFlow" yField="percentCv"
                                     markerType="circle" markerSize="2" markerFill="#0063ff"/>
                    <apex:lineSeries smooth="4" title="Valve Inherent Characteristic" axis="left" xField="percentCv" yField="requiredCv" 
                                     markerType="circle" markerSize="3" markerFill="#00000"/>
                </apex:chart>
                 <canvas id="canvas" style="display:none;"></canvas>
            </div>
            <apex:image value="{!attach.Body}" rendered="true" />

Best Answer

The best approach using best practices for SF objects embedding in VF pages would be not to use any static resource, but to embed the saved attachment itself in the page, with some minor tweaks :

  1. In your saveChartAsAttachment @RemoteAction make sure you preserve the Attachment Id in some static variable and also make it available in some public variable in your page controller (AttachmentId), so it can be used in the VF page, at the same time you store the image data in it:

    public static ID AttachmentId {get; set;}
    
    @RemoteAction
    global static void saveChartAsAttachment(String prjId,String attachmentBody) {
    
        system.debug('Project ID: '+ prjId);
        List<Attachment> listAttachment =[Select Name,Id,Body from Attachment Where ParentId=:prjId];
        system.debug('Size of Attachment: '+listAttachment.size());
        Attachment att;
    
        if(listAttachment.size() > 0) {
           att =listAttachment[0];
           att.Body = EncodingUtil.base64Decode(attachmentBody);
           update att;
           AttchamentId = att.Id;
    
        } else {
            att = new Attachment();
            att.Name='spiderChart_VF';
            att.ParentId=prjId;
            att.ContentType='image/png';
            att.Body = EncodingUtil.base64Decode(attachmentBody);
            insert att;
    
            List<Attachment> insAtts =[Select Name,Id from Attachment Where ParentId=:prjId];
            Attachment insAtt;
            if(insAtts.size() > 0){
                insAtt = insAtts[0];
                AttchamentId = insAtt.Id;
            }
        }
    }
    
  2. In your VF page, change the apex:image declaration using the $Action markup to display the Attachment content by Id. This will display the chart image saved as attachment in the page, ready to be rendered as PDF, provided the image was properly saved by the script above:

    <script>
        function savePNG(){
    
        var mainDiv=document.getElementById('chart_div');
        var svg = mainDiv.children[0].children[0].innerHTML;
        console.log('@@svg .... ' + svg);
        canvg(document.getElementById('canvas'),svg);
        var img = canvas.toDataURL("image/png"); //img is data:image/png;base64
        img = img.replace('data:image/png;base64,', '');
        var data = "bin_data=" + img;
        alert(data);
        alert(img);
    
        Visualforce.remoting.Manager.invokeAction('{!$RemoteAction.VWSizingReport02Controller.saveChartAsAttachment}',
       '{!Proj.ID}',img,
          function(result,event){
             if(event.status){
             alert("DOne");
               console.log('@@@ success');
    
             }
             else {
               console.log('@@@ fail');
             alert("Error");
             }
          } );
    }
    </script>
    <input type="button" id="save_img_att" value="Save as Attachment" onclick="savePNG();"/>   
        <div id="chart_div">
           <p class="valve-notes" >
                <label class="valve-notes">Flow Curve Graph:</label>
            </p>
    
            <apex:chart animate="false" resizable="true" height="480" width="680" data="{!chartData}">
                <apex:legend position="bottom"/>
                <apex:axis steps="10" type="Numeric" position="left" fields="percentCv" 
                           title="% Cv"/>        
                <apex:axis steps="5" maximum="100" type="Numeric" position="bottom" fields="percentOpen" 
                           title="% Open"/>
                <apex:lineSeries title="Maximum Flow" axis="left"  xField="maximumFlow" yField="percentCv"
                                 markerType="circle" markerSize="2" markerFill="#FF0000"/>
                <apex:lineSeries title="Normal Flow" axis="left"  xField="normalFlow" yField="percentCv"
                                 markerType="circle" markerSize="2" markerFill="#17ff00"/>
                <apex:lineSeries title="Minimum Flow" axis="left"  xField="minimumFlow" yField="percentCv"
                                 markerType="circle" markerSize="2" markerFill="#0063ff"/>
                <apex:lineSeries smooth="4" title="Valve Inherent Characteristic" axis="left" xField="percentCv" yField="requiredCv" 
                                 markerType="circle" markerSize="3" markerFill="#00000"/>
            </apex:chart>
             <canvas id="canvas" style="display:none;"></canvas>
        </div>
        <apex:image url="{!URLFOR($Action.Attachment.Download, AttachmentId)}"/>
    

This should also allow you to specify additional properties like size, placement, etc. and render it in PDF directly from the saved attachment.

Related Topic