Creating a CSV file blob in Apex
My recommendations:
- use Batch Apex, passing in the initial
Lists
as stateful variables using Database.Stateful
, and then creating a CSV row out of each SObject during the Batch Apex execute
method. In the finish
method of your Batch Apex, send an email containing the generated CSV. Using Batch Apex to do the CSV row generation will avoid the Too Many Script Statements
error, since Batch Apex limits are reset for each Batch Apex execute
method invocation. The resultant CSV file can be stored as a String instance variable, and converted to a Blob in the finish
method, then emailed to the user requesting the export.
- Create the blob client-side using JavaScript (no script statement limits there!), and sent to Apex using one of the following
- JS Remoting
- VF ActionFunction
- HTTP POST Request
Example of Batch Apex
Here is some sample code for the JavaScript method:
To create CSV fields in JavaSCript, here's some a method that will work:
// Escapes a String such that it can be used in a CSV cell
// Based on Apache Commons StringEscapeUtils library
// (see Wikipedia and RFC 4180)
// Parameters:
// input - the input CSV column String, may be null
// Returns:
// the input String, enclosed in double quotes
// if the value contains a comma, newline or double quote,
// '' if null string input.
// Also, any double quote characters in the value
// are escaped with another double quote.
var QUOTE = '"',
ESCAPED_QUOTE = "\"\"",
CHARS_THAT_MUST_BE_QUOTED = [ ',' , '"' , '\n' ];
var escapeCSVCell = function(c) {
if ( c.indexOf( QUOTE ) > -1 ) c = c.replace(/"/g, ESCAPED_QUOTE );
$.each(CHARS_THAT_MUST_BE_QUOTED,function(i,char){
if (c.indexOf(char) > -1) {
c = QUOTE + c + QUOTE;
return false;
}
});
return c;
}
// Call escapeCSVCell on each CSV field
Parsing CSV files in Apex
Number One: Do not roll your own CSV Parser: use someone else's! There are a lot of complexities to parsing IETF RFC 4180 - compliant CSV files that you will uncover, one after another, if you try to do roll your own parser. Trust me, not worth it.
And guess what? You don't have to!
Marty Chang has done the Salesforce/Apex developer commmunity a huge service by writing a rock-solid, IETF RFC 4180 compliant CSV Parser FOR APEX!!!
Here is the link to download the 2 files he wrote for this purpose, CSVParser and CSVReader. I've used it before, trust me, it handles everything, even newlines embedded in your CSV fields (try writing that yourself --- NOT straightforward.)
Here's how to use it:
// (Only necessary if your CSV file starts as a string)
Blob csvBlob = Blob.valueOf(csvString);
// Convert your csv file into a list of CSV fields
List<List<String>> fieldsByLine = CSVParser.readIETFRFC4180CSVFile(csvBlob);
Yeah, it's that easy!
His solution uses some good RegEx to bypass the "too many statements" limit.
Creating SObject records from CSV Rows in Apex
As far as creating records from these CSV rows, i've done this before using Batch Apex. Convert your CSV files into rows during the Batch Apex job and create rows one-by-one in the execute method. Also, shameless plug, there's actually an IETF RFC 4180-compliant CSV Import Wizard built-in to Skuid, so if you are open to using an external library, why roll your own parser?
Parsing CSV Rows in Apex
Winter 13 (API v26) has some awesome methods for escaping/unescaping CSV fields:
String.escapeCsv(String textToMakeIntoACSVField)
String.unescapeCsv(String csvField)