1

Here is what I'm trying to achieve:

  1. Upload a csv file on the click of "Upload File" button. ReadFile() method is invoked
  2. On click of "Action" button, all the row data will be uploaded to a custom object. Processing() method is invoked (not included in the code below, but it uses "emailList" List).

Here's the code for my controller class:

public class FileUploaderTest {

    public transient string nameFile {get;set;}
    public transient blob contentFile {get;set;}
    public transient string[] fileLines = new string[]{};
    public List<string> emailList = new List<string>();


        public static String blobToString(blob input, String inCharset){
        //code to convert blob to string.
    }

    public Pagereference ReadFile(){
        nameFile =blobToString(contentFile,'ISO-8859-1');
        filelines = nameFile.split('\n');
                for (Integer i=0;i<filelines.size();i++)
                {
                    emailList.add(filelines[i]);
                }
        return null;
    } 
    public Pagereference processing(){
    //Uses emailList List and logic is performed based on the elements of this list.
}

Everything is working as expected but I'll be facing the issue when the csv file is large. This is because "emailList" is a part of view state. I cannot make emailList transient because I'm getting null pointer exception error. I tried making it static but oddly, no data comes into the list and the list appears to be empty.

So is there is any way I can exclude emailList from viewstate? Or, is it possible to make a copy of it within the class and clear emailList in ReadFile() itself to clear the viewstate?

1

static behaves a lot like transient, in that they are not stored in the view state. The difference is that static variables are initialized each time the class is accessed, which is why the list appeared blank. If you need to be able to show the data in emailList, like a preview mode, then you need to have the data somewhere, it can't just be inside ReadFile or the data would still disappear between transactions.

If you insist on using Apex Code for this purpose, you'd need to keep the data somewhere safe, like inside a postback value or in the database somewhere. Unfortunately, this will incur some additional serialization and deserialization overhead (basically, you're reprocessing the CSV each time). Basically, would look something like this:

transient List<String> tempEmailList;
public List<String> emailList { get { return tempEmailList; } }
public String emailStorage { 
  get { return tempEmailList == null? null: JSON.serialize(tempEmailList); }
  set { tempEmailList = value == null? null: (List<String>)JSON.deserialize(value, List<String>.class); }
}

Which you would then hide inside the postback:

<apex:inputHidden value="{!emailStorage}" />

If this looks a bit wonky... well, it is. Typically, once you get to the point where you need more than 100kb of View State, you need to start looking at alternative methods. I would probably recommend using client-side parsing/rendering using JavaScript, then actually process the database actions using a @RemoteAction method.

If you have access to it, using the Platform Cache may also be a viable alternative, but since it is volatile, your users might have to periodically re-upload the data if they take too long reviewing it.

So, as another alternative, you might decide to just store the CSV file in a processed form (e.g. JSON.serialize to a Document file), so you can maintain the state as a record in the database until the work has been completed. This design would also give you the option to allow users to resume an incomplete attempt.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.