Upload a CSV and parse the data both UTF-8 and non UTF-8 File Salesforce:
There were many instances in my career where I had to built a custom bulk upload functionality. I always took the SFDC option to input a CSV file and then parse the file into List<List<String, string>>.
The steps involved to in these type of custom upload was:
1. Input a file using apex:input and bind with a blob type to controller.
VF :
<apex:input value="fileContentBlob">
Contoller :
Public Blob fileContentBlob {get; set;}
2. Convert the Blob into String.
If Uploaded file is in "UTF-8" (Csv with no special Characters).
String fileContentText= fileContentBlob.toString();
Note : If non UTF-8 file is passed to above code, it will throw String Exception.
If Uploaded file is not in "UTF-8" (CSV with special characters).
String fileContectText = blobToString(fileContentBlob);
public String blobToString(Blob blobToConvert)
{
String hex = EncodingUtil.convertToHex(input);
final Integer bytesCount = hex.length() >> 1;
String[] bytes = new String[bytesCount];
for(Integer i = 0; i < bytesCount; ++i)
bytes[i] = hex.mid(i << 1, 2);
return EncodingUtil.urlDecode('%' + String.join(bytes, '%'), 'UTF-8');
}
Note : This secound approch to convert Blob to string is heavy on CPU, But this work very well for CSV with special charavters. Hence use this approach only in case where User file can have special characters.
3. Parse the String to get value of individual cell in CSV.
To do this I have a common code (found by google). find the Apex class below "CSV_Parser".Controller :
List<List<String>> fileRows=new CSV_Parser().parseCSVContent(fileContectText,true);
Note : CSV_Parser class code is given below in sample code.
Code Sample:
Please find the sample code which will take a CSV input (with special Characters). The input file is parsed and data in the CSV is displayed on the page as a table.
VF Page :
<apex:page controller="UploadTest_CTRL"> <apex:pagemessages /> <apex:form > <apex:inputFile value="{!contentFile}" styleclass="btn" style="display:show;" onchange="setFileName();"/> <apex:commandButton styleClass="btn" action="{!ReadFile}" value="Upload File" id="theButton" style="display:show;"/> <br/> <br/> File Size : {!fileLines.size} </apex:form> <table> <apex:repeat value="{!fileLines}" var="line"> <tr> <apex:repeat value="{!line}" var="linecell"> <td> {!linecell} </td> </apex:repeat> </tr> </apex:repeat> </table> </apex:page>
Controller Code :
public class UploadTest_CTRL { public List<List<String>> fileLines {get; set;} public String nameFile{get;set;} public Blob contentFile{get;set;} Public String ErrorMessage {get;set;} public UploadTest_CTRL() { fileLines = new List<List<String>>(); } public void readFile() { try { System.debug('contentFile '+contentFile); nameFile = blobToString(contentFile,'UTF-8'); //blob b = EncodingUtil.base64Decode(nameFile); //nameFile = b.toString(); } Catch(System.StringException stringException) { ApexPages.Message errormsg = new ApexPages.Message(ApexPages.severity.Error,Label.Upload_Case_String_Exception_Error + stringException); ApexPages.addMessage(errormsg); } CSV_Parser parseCSVInstance = new CSV_Parser(); try { filelines = parseCSVInstance.parseCSVContent(nameFile, true);
// Custom logic goes here . Have logic to process the data like creating a record .
} Catch(System.Exception exp) { ApexPages.Message errormsg = new ApexPages.Message(ApexPages.severity.Error,'Exception' + exp); ApexPages.addMessage(errormsg); } } public static String blobToString(Blob input, String inCharset){ String hex = EncodingUtil.convertToHex(input); //System.assertEquals(0, hex.length() & 1); final Integer bytesCount = hex.length() >> 1; String[] bytes = new String[bytesCount]; for(Integer i = 0; i < bytesCount; ++i) bytes[i] = hex.mid(i << 1, 2); return EncodingUtil.urlDecode('%' + String.join(bytes, '%'), inCharset); } }
Helper Class CSV_Parser
public class CSV_Parser { /** Method Name : parseCSVContent Return Type : String contents,Boolean skipHeaders Description : Business logic for parsing the string */ public List<List<String>> parseCSVContent(String contents,Boolean skipHeaders) { List<List<String>> allFields = new List<List<String>>(); // replace instances where a double quote begins a field containing a comma // in this case you get a double quote followed by a doubled double quote // do this for beginning and end of a field contents = contents.replaceAll(',"""',',"DBLQT').replaceall('""",','DBLQT",'); // now replace all remaining double quotes - we do this so that we can reconstruct // fields with commas inside assuming they begin and end with a double quote contents = contents.replaceAll('""','DBLQT'); // we are not attempting to handle fields with a newline inside of them // so, split on newline to get the spreadsheet rows List<String> lines = new List<String>(); try { lines = contents.split('\r\n'); } catch (System.ListException e) { System.debug('Limits exceeded?' + e.getMessage()); } system.debug(lines+'lines'); Integer num = 0; for(String line : lines) { system.debug('line'+line); // check for blank CSV lines (only commas) if (line.replaceAll(',','').trim().length() == 0) continue; List<String> fields = line.split(','); system.debug(fields); List<String> cleanFields = new List<String>(); String compositeField; Boolean makeCompositeField = false; for(String field : fields) { if (field.startsWith('"') && field.endsWith('"')) { cleanFields.add(field.replaceAll('DBLQT','"')); } else if (field.startsWith('"')) { makeCompositeField = true; compositeField = field; } else if (field.endsWith('"')) { compositeField += ',' + field; cleanFields.add(compositeField.replaceAll('DBLQT','"')); makeCompositeField = false; } else if (makeCompositeField) { compositeField += ',' + field; } else { cleanFields.add(field.replaceAll('DBLQT','"')); } } allFields.add(cleanFields); } system.debug(allFields.size()+'allFields'); system.debug(allFields+'allFields'); if (skipHeaders) allFields.remove(0); system.debug(allFields+'allFields'); system.debug(allFields.size()+'allFields'); return allFields; } public static String blobToString(Blob input) { String hex = EncodingUtil.convertToHex(input); final Integer bytesCount = hex.length() >> 1; String[] bytes = new String[bytesCount]; for(Integer i = 0; i < bytesCount; ++i) bytes[i] = hex.mid(i << 1, 2); return EncodingUtil.urlDecode('%' + String.join(bytes, '%'), 'UTF-8'); } }