0

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" />
4
  • Use documents or attachments if possible
    – Raul
    Commented May 18, 2017 at 12:38
  • Thanks for your response @RahulSharma I'm using attachment [standard object] to save and update. Please check the above code. Commented May 18, 2017 at 12:41
  • @ShoaibKhan - So is your question how to save an attachment or how to create/update a static resource? Please update your question if it i no longer related to static resources
    – Eric
    Commented May 18, 2017 at 13:35
  • @Eric Thanks for your time. As i mentioned in question, i have a apex:chart that i want to render it into pdf. But an apex:chart cannot be rendered into pdf. You know this. So i have created an image of that chart using some libraries, as mentioned in the VF code. Also created a Static Resource named FC_Chart. I want to update the value of this static resource and wanna use it in VF which is easy to be rendered in pdf. Problem is i'm unable to update/insert the value of static resource. Commented May 19, 2017 at 6:24

1 Answer 1

1

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.

6
  • Hey @razvan Thanks for your comments and help. I tried your solution. But i got this error. Error: Error occurred while loading a Visualforce page. Error: Invalid parameter for function URLFOR Commented May 23, 2017 at 7:26
  • That is most likely caused by the AttachmentId parameter not being accessible to your page.
    – Razvan
    Commented May 23, 2017 at 17:49
  • Actually you should get another error if AttachmentId would not be accessible to your page, and yet another if it would be set to an invalid value. This error means the AttachmentID linked to your page is simply not set (null). Try to debug output its value from the code at the end of the method that's setting it, and make sure the AttachmentId variable declared next to your RemoteAction method is actually inside your page controller, and you don't have another variable with the same name in your page controller that's never being set.
    – Razvan
    Commented May 23, 2017 at 18:04
  • If the RemoteAction method is not located in your page controller then neither is the static AttachmentId and it can't be used by the page. In that case you need to find a way to get that value in your page controller. If the RemoteAction method and the static AttachmentId are located in the page controller, then for some reason it's not being set, or it's being lost / reset to null between the time you save it in your remote action and the page reloads to display image content.
    – Razvan
    Commented May 23, 2017 at 18:14
  • Another idea is that if you try to render the image right at page load, before the RemoteAction is executed, then for sure the AttachmentId won't be set at that time. You need to only render the image after the RemoteAction has been executed and AttahmentId has been set. You can use rendered="{!NOT(ISBLANK(AttachmentId))" on the apex:image, and do a window.location.reload(); after your alert("DOne"); in javascript. That should work though there are more elegant ways to only refresh needed components not the entire page.
    – Razvan
    Commented May 23, 2017 at 18:23

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .