[SalesForce] Upload File and Display data on Visualforce page more than 10K

I am trying to upload a file from a visualforce page and trying to display the same after Inserting it in the page. The file that i am trying to upload is a CSV file and the data in the file could be more than 10K. When i try to add paging to the data I get a "Modified rows exist in the records collection" error. when i try to display the same without the paging I get a View state error.

I have added the code below.

<apex:page sidebar="false" controller="FileUploader" showHeader="false">
   <apex:form >
      <apex:sectionHeader title="Upload data from CSV file"/>
      <apex:pagemessages />
      <apex:pageBlock >
             <!--  Component to allow user to upload file from local machine -->
             <center>
              <apex:inputFile value="{!contentFile}" filename="{!nameFile}" /> <apex:commandButton action="{!ReadFile}" value="Upload File" id="theButton" style="width:70px;"/>
              <br/> <br/> <font color="red"> <b>Note: Please use the standard template to upload Accounts. <a href="https://drive.google.com/file/d/0B5FQvDdE4z0PdFllT1g0aGNBN1k/view?usp=sharing" target="_blank"> Click here </a> to download the template. </b> </font>
             </center>  

      <!-- After the user clicks the 'Upload File' button, this section displays the inserted data -->
      <apex:pageblocktable value="{!uploadedAccounts}" var="acc" rendered="{!NOT(ISNULL(uploadedAccounts))}">
          <apex:column headerValue="Supplier Number">
              <apex:outputField value="{!acc.Supplier_Number__c}"/>
          </apex:column>
          <apex:column headerValue="Supplier Name">
              <apex:outputField value="{!acc.Name}"/>
          </apex:column>
          <apex:column headerValue="supplier Type">
              <apex:outputField value="{!acc.Supplier_Type__c}"/>
          </apex:column>
      </apex:pageblocktable>

      </apex:pageBlock>       
   </apex:form>   
</apex:page>

And the Controller for the above Visualforce page is:

public class FileUploader 
{
    public string nameFile{get;set;}
    public Blob contentFile{get;set;}
    String[] filelines = new String[]{};
    public Integer rowNum{get;set;}
    public Transient Map<Integer, PQN_Supplier__c> accstoupload{get;set;}
    public Boolean load{get;set;}

    public ApexPages.StandardSetController con {
        get {
                if(con == null) {
                    if (load){
                        con = new ApexPages.StandardSetController(accstoupload.values()); 
                        con.setPageSize(500);               
                    }       
                }
                return con;
        }
        set;
    }

    public FileUploader(){
        load = False;
    }

    public Pagereference ReadFile()
    {
        try{
                //Convert the uploaded file which is in BLOB format into a string
                nameFile =blobToString( contentFile,'ISO-8859-1');

                //Now sepatate every row of the excel file
                filelines = nameFile.split('\n');

                Integer index = 0;

                accstoupload = new Map<Integer, PQN_Supplier__c>();
                for (Integer i=1;i<filelines.size();i++)
                {
                    String[] inputvalues = new String[]{};
                    inputvalues = filelines[i].split(',');

                    PQN_Supplier__c a = new PQN_Supplier__c();
                    a.Supplier_Number__c = inputvalues[0];
                    a.Name = inputvalues[1];       
                    a.Supplier_Type__c = inputvalues[2];

                    index = index + 1;
                    accstoupload.put(index, a);
                }
                filelines = Null;
         }
         catch(Exception e){
                ApexPages.Message errormsg = new ApexPages.Message(ApexPages.severity.ERROR,'An error has occured reading the CSV file'+e.getMessage());
                ApexPages.addMessage(errormsg);
         }       
        //Finally, insert the collected records
        try{
            insert accstoupload;
            accstoupload = Null;
            load = True;
        }
        catch (Exception e)
        {
            ApexPages.Message errormsg = new ApexPages.Message(ApexPages.severity.ERROR,'An error has occured inserting the records'+e.getMessage());
            ApexPages.addMessage(errormsg);
        }    
        return null;
    }

   /**** This function sends back to the visualforce page the list of account records that were inserted ****/ 
    public List<PQN_Supplier__c> getuploadedAccounts()
    {   
        if (load)
            return (List<PQN_Supplier__c>)con.getRecords();
        else
            return Null;
    } 
        /**
        This function convers the input CSV file in BLOB format into a string
        @param input    Blob data representing correct string in @inCharset encoding
        @param inCharset    encoding of the Blob data (for example 'ISO 8859-1')
     */
    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);
    }         
}

Please help me fix this.

Thanks

Best Answer

Unless I've missed something, marking public Blob contentFile{get;set;} with the transient keyword would presumably resolve your viewstate error provided you don't try to paginate. The page shouldn't need to refresh to maintain the contents of that variable once you do your string conversion using the encoding utility in the public static String blobToString(Blob input, String inCharset) method.

Edit

As the above didn't seem to solve your issue for you, it seems to me that you need to separate you concerns here. It appears that the thing to do is:

  1. Upsert the data using Database.UpsertResult method so you'll be able to collect the Ids of the resulting saved records.

  2. Query the results from the Upsert to then display on the page using pagination.

In essence you make this a 2 stage operation. That will make things a bit slower in loading, but should also make it more reliable and give you a smoother running page once the upsert has completed.

Related Topic