• 换个角度看Salesforce之利用Zippex结合Document生成ZIP文件(三十六)


    1. Zippex相关文件的下载地址

        https://github.com/pdalcol/Zippex

    2. 调用页面及对应Controller方法

        <apex:commandButton value=" Export Catalog/Schema by batch" onclick="exportDataFile()"/>

        JS方法:

    function exportDataFile()
    {
        try {
            sforce.connection.sessionId = "{!$Api.Session_ID}";
            var result = sforce.apex.execute(
                'PC_MyClassA',
                'createZipFile', 
                {
                    genType:'All'
                }
            );
            //alert(result);
            if (result!='false') {
                /*var url = URL.createObjectURL(new Blob(result));
                var downloadAnchorNode = document.createElement('a');
                downloadAnchorNode.setAttribute("href", url);
                downloadAnchorNode.setAttribute('download',result);
                downloadAnchorNode.click();
                downloadAnchorNode.remove();*/
                alert('The zip file('+result+') is ready,please get it from Document tab(Shared document folder),thanks!');
            }
            else
            {
                alert('There is some issue with the process, please check!');
            }
        }
        catch (e)
        {
            alert(e);
        }
    }
    View Code

        Controller方法:

    WebService
    static String createZipFile(String genType)
    {
        try
        {
            Database.executeBatch(new PC_BatchCreateJsonFile(),1);
            
            /*Zippex zip = new Zippex();
            String clJson='';
            List<PC_DataSourceObject__c> dsList=[SELECT Id, DataSourcePK__c FROM PC_DataSourceObject__c LIMIT 10];
            for(PC_DataSourceObject__c ds:dsList)
            {
                clJson= PC_GenJsonFile.createObjectJson(ds.DataSourcePK__c);
                zip.addFile(pc.DataSourcePK__c+'.json', Blob.valueOf(clJson), null);
            }
            
            DateTime di = Datetime.Now();
            String fileName='CatZip_'+di.year()+di.month()+di.day()+di.hour()+di.minute()+di.millisecond()+'.zip';
            Blob zipBlob = zip.getZipArchive();
            Document d = new Document(); 
            d.folderid='00l10000001FnWcAAK';
            d.Name=fileName;
            d.body=zipBlob;
            insert d;
            return filename;*/
            return 'true';
        }
        catch(Exception ex)
        {
            return 'false';
        }
    }
    View Code

    3. 创建可以生成JSON文件的Batch Apex Job

    public class PC_BatchCreateJsonFile implements Database.Batchable<SObject>, Database.AllowsCallouts {
        String proCatDec='BatchCreateJSON';
        public Iterable<SObject> start(Database.BatchableContext BC) {
            
            List<PC_DataSourceObject__c> dsList=[SELECT Id, DataSourcePK__c FROM PC_DataSourceObject__c ORDER BY DataSourcePK__c ASC limit 1];
            return dsList;
        }
    
        public void execute(Database.BatchableContext BC, List<PC_DataSourceObject__c> dsList) {
            /*String clJson='';
            List<Document> docList = new List<Document>();
            
            for(PC_DataSourceObject__c ds:dsList)
            {
                clJson= PC_ProductEngineAdaptor.createJson(ds.DataSourcePK__c);
                Document doc = new Document();
                doc.folderid='00l10000001FnWcAAK';
                doc.Name=pc.DataSourcePK__c+'.json';
                doc.body=Blob.valueOf(clJson);
                doc.description=proCatDec;
                docList.add(doc);
            }
    
            insert docList;*/
        }
    
        public void finish(Database.BatchableContext BC) {
            DateTime di = Datetime.Now();
            String fileName='CatZip_'+di.year()+di.month()+di.day()+di.hour()+di.minute()+di.millisecond()+'.zip';
            Database.executeBatch(new PC_GenerateZipFile(proCatDec,fileName),10);
        }
    }
    View Code

    4. 创建可以生产ZIP文件的Batch Apex Job

    public class PC_GenerateZipFile implements Database.Batchable<SObject>, Database.AllowsCallouts,Database.Stateful {
        private String OperateType='';
        private String fileName='';
        private Zippex zip = new Zippex();
        public PC_GenerateZipFile(String OperateType,String fileName)
        {
             this.OperateType = OperateType;
             this.fileName = fileName;
        }
        
        public Iterable<SObject> start(Database.BatchableContext BC) {
            
            List<Document> docList = [SELECT ID,Name,Body FROM Document WHERE description =: OperateType];
            return docList;
        }
    
        public void execute(Database.BatchableContext BC, List<Document> docList) {
            
            for(Document doc:docList)
            {
                zip.addFile(doc.Name,doc.Body,null);
            }
        }
    
        public void finish(Database.BatchableContext BC) {
            try
            {
                Document d = new Document(); 
                d.folderid='00l10000001FnWcAAK';
                d.Name=fileName;
                d.body=zip.getZipArchive();
                d.Description=OperateType+'- Zip - '+Datetime.now();
                //delete docList;
                insert d;
            }
            catch(Exception ex)
            {
                
            }
        }
    }
    View Code

    5. 下载文件

        5.1 可以从Document Tab上下载已经生成好的ZIP文件;

        5.2  也可以根据生成的ZIP文件的DocumentID,通过以下代码生成的URL自动下载;

    List<Document> docList =[SELECT ID FROM Docuemnt WHERE Name like '%.zip'];
    String strURL;
    for(Document doc : docList)
    {
         strURL = URL.getSalesforceBaseUrl().toExternalForm() + '/servlet/servlet.FileDownload?file='+ doc.id;
         System.debug('strURL ===> '+strURL);
    }
    View Code

    6. 其他生成ZIP文件的方法:

        6.1 通过JS下载文件:

        6.2 创建基于某个对象记录的附件:

    Attachment at = new Attachment();
    
    at.ParentId='001N000001OIAsP';
    
    at.Name='case.zip';
    
    at.body=zipBlob;
    
    insert at;
    View Code

        6.3 创建Document:

    Document d = new Document();
    
    d.folderid='00l10000001FnWcAAK';
    
    d.Name='case.zip';
    
    d.body=zipBlob;
    
    insert d;
    View Code

    7. 总结

        通过测试,该例子在文件数据较小时尚可,一旦ZIP中包含的文件太多,还会报出‘Apex CPU Time Exceeded’错误。经过调试,主要问题是CRC32Table(String hexStr) 用时太多的缘故。我的想法是能不能通过优化该方法来实现突破!

    8. 补充1:

     根据第七点的结论,经过查看Zippex原代码,发现我们需要使用的方法addFile()含有第三个参数,而这个参数正是我后面需要生成的CRC32Table()方法的返回结果。所以我将该方法中的内容重新在Batch Job中的execute()方法中重新做了实现,这样最大限度地利用了Batch Job的优势,从而成功地实现了我的要求。参见修改后的代码:

    public class PC_GenerateZipFile implements Database.Batchable<SObject>, Database.AllowsCallouts,Database.Stateful {
        private String OperateType='';
        private String fileName='';
        private Zippex zip = new Zippex();
        public PC_GenerateZipFile(String OperateType,String fileName)
        {
             this.OperateType = OperateType;
             this.fileName = fileName;
        }
        
        public Iterable<SObject> start(Database.BatchableContext BC) {
            
            List<Document> docList = [SELECT ID,Name,Body FROM Document WHERE description =: OperateType];
            return docList;
        }
    
        public void execute(Database.BatchableContext BC, List<Document> docList) {
            
            for(Document doc:docList)
            {
                Integer b = 0;
                Integer crc = -1;
                String hexStr=EncodingUtil.convertToHex(doc.Body);
                Integer size = hexStr.length();
                
                for (Integer i = 0; i < size; i+=2)
                {
             
                    b = ((hexStr.charAt(i) & 15)+(hexStr.charAt(i)>>>6)*9) * 16
                      | (hexStr.charAt(i+1) & 15)+(hexStr.charAt(i+1)>>>6) * 9;
                    crc = (crc >>> 8) ^ HexUtil.table[(crc ^ b) & 255];
        
                }
                crc = crc ^ -1;
                zip.addFile(doc.Name,doc.Body,HexUtil.intToHexLE(crc,4));
            }
        }
    
        public void finish(Database.BatchableContext BC) {
            try
            {
                Document d = new Document(); 
                d.folderid='00l10000001FnWeAAK';
                d.Name=fileName;
                d.body=zip.getZipArchive();
                d.Description=OperateType+'- Zip - '+Datetime.now();
                //delete docList;
                insert d;
            }
            catch(Exception ex)
            {
                
            }
        }
    }
    View Code

    9. 补充2:

         根据上述7,8点,我的第一个应用已经基本实现。但在第二个应用中,不论是文件的数目还是文件的大小都有了一定的变化。经过深入思考,我准备把需要的ZIP文件拆分为几个较小的ZIP文件:

         方案一 - 每100个文件生成一个ZIP文件:

    public class PC_GenerateZipFile implements Database.Batchable<SObject>, Database.AllowsCallouts,Database.Stateful {
        private String OperateType='';
        private String fileName='';
        private Zippex zip = new Zippex();
        integer m=0;
        public PC_GenerateZipFile(String OperateType,String fileName)
        {
             this.OperateType = OperateType;
             this.fileName = fileName;
        }
        
        public Iterable<SObject> start(Database.BatchableContext BC) {
            
            List<Document> docList = [SELECT ID,Name,Body FROM Document WHERE description =: OperateType ORDER BY CreatedDate DESC];
            return docList;
        }
    
        public void execute(Database.BatchableContext BC, List<Document> docList) {
            integer n=100;
            for(Document doc:docList)
            {
                Integer b = 0;
                Integer crc = -1;
                String hexStr=EncodingUtil.convertToHex(doc.Body);
                Integer size = hexStr.length();
                
                for (Integer i = 0; i < size; i+=2)
                {
             
                    b = ((hexStr.charAt(i) & 15)+(hexStr.charAt(i)>>>6)*9) * 16
                      | (hexStr.charAt(i+1) & 15)+(hexStr.charAt(i+1)>>>6) * 9;
                    crc = (crc >>> 8) ^ HexUtil.table[(crc ^ b) & 255];
        
                }
                crc = crc ^ -1;
                zip.addFile(doc.Name,doc.Body,HexUtil.intToHexLE(crc,4));
                m++;
                if(m >= n)
                {
                    Document d = new Document(); 
                    d.folderid='XXXXXXXXXXXXXX';
                    d.Name=fileName;
                    d.body=zip.getZipArchive();
                    d.Description=OperateType+'- Zip - '+Datetime.now();
                    //delete docList;
                    insert d;
                    zip = new Zippex();
                    m=0;
                }
            }
        }
    
        public void finish(Database.BatchableContext BC) {
            try
            {
                /*Document d = new Document(); 
                d.folderid='XXXXXXXXXXXXXX';
                d.Name=fileName;
                d.body=zip.getZipArchive();
                d.Description=OperateType+'- Zip - '+Datetime.now();
                //delete docList;
                insert d;*/
            }
            catch(Exception ex)
            {
                
            }
        }
    }
    View Code

      Ps. 由于刚开始不太了解Batch Apex Job的运行机制,刚开始将变量m定义在了execute()方法中,结果总是达不到自己预想;

        方案二 - 每个ZIP文件大小限制为2.5M(和文件数目无关):

    public class PC_GenerateZipFile implements Database.Batchable<SObject>, Database.AllowsCallouts,Database.Stateful {
        private String OperateType='';
        private String fileName='';
        private Zippex zip = new Zippex();
        public PC_GenerateZipFile(String OperateType,String fileName)
        {
             this.OperateType = OperateType;
             this.fileName = fileName;
        }
        
        public Iterable<SObject> start(Database.BatchableContext BC) {
            
            List<Document> docList = [SELECT ID,Name,Body FROM Document WHERE description =: OperateType ORDER BY CreatedDate DESC];
            return docList;
        }
    
        public void execute(Database.BatchableContext BC, List<Document> docList) {
            Decimal fileSize= 2.5 * 1024 *1024;
            for(Document doc:docList)
            {
                Integer b = 0;
                Integer crc = -1;
                String hexStr=EncodingUtil.convertToHex(doc.Body);
                Integer size = hexStr.length();
                
                for (Integer i = 0; i < size; i+=2)
                {
                    b = ((hexStr.charAt(i) & 15)+(hexStr.charAt(i)>>>6)*9) * 16
                      | (hexStr.charAt(i+1) & 15)+(hexStr.charAt(i+1)>>>6) * 9;
                    crc = (crc >>> 8) ^ HexUtil.table[(crc ^ b) & 255];
                }
                crc = crc ^ -1;
                zip.addFile(doc.Name,doc.Body,HexUtil.intToHexLE(crc,4));
                Blob ccBody=zip.getZipArchive();
                if(ccBody.size() > fileSize)
                {
                    Document d = new Document(); 
                    d.folderid='XXXXXXXXXXXXX';
                    d.Name=fileName;
                    d.body=ccBody;
                    d.Description=OperateType+'- Zip - '+Datetime.now();
                    insert d;
                    zip = new Zippex();
                }
            }
        }
    
        public void finish(Database.BatchableContext BC) {
            try
            {
                /*Document d = new Document(); 
                d.folderid='XXXXXXXXXXXXX';
                d.Name=fileName;
                d.body=zip.getZipArchive();
                d.Description=OperateType+'- Zip - '+Datetime.now();
                //delete docList;
                insert d;*/
            }
            catch(Exception ex)
            {
                
            }
        }
    }
    View Code

     10. 补充3:

           在使用Apex Job时,最好在Start方法中使用DataBase.getQueryLocator()标准返回,如果返回常见List,极易导致异常‘System.LimitException: Apex heap size too large’,并且处理速度也会很慢;

  • 相关阅读:
    清理weblogic缓存
    [转载]哪些行为让你觉得对方很高级很有教养?
    [转载]哪些行为让你觉得对方很高级很有教养?
    Linux系统信息查看命令大全
    Linux系统信息查看命令大全
    [转载]永远保持随时可以离开的能力(不仅仅是张泉灵)
    [转载]永远保持随时可以离开的能力(不仅仅是张泉灵)
    2015面试的技术点
    movie maker精准到1/100秒
    vs调试看窗口风格
  • 原文地址:https://www.cnblogs.com/sccd/p/15245008.html
Copyright © 2020-2023  润新知