How to let users upload CSV files to batch import data


#1

Hey guys,

I am trying to implement a “batch import” feature on my app that allows users to import data through a .csv file that automatically adds them to a Mongo Collection. I am using the clinical:csv package.

However, I can’t figure out how to get the part that comes before the Mongo insertion done…

How do I create an “import your file here” button that opens the user’s file explorer and gets the file?
How do I check if the uploaded file is truly .csv and in the right format?

Any ideas? Thanks!


#2

https://www.papaparse.com is the best library to handle CSV.

Others links:


#3

Hi @patrickcneuhaus,

I use papaparse to parse the csv into json, Ill post a code example of how I do it using react. I havent tested this I just extracted parts of my code for this example.

To validate the data I do it on the method using simpleschema, bubt you can also do it with simple schema on the _importUsersBatch function.

class MyView extends Component {
  _importUsersBatch = async (event) => {
    event.preventDefault();

    this.setState({ errors: [], loading: true });

    const file = event.target.files[0];
    const results = await parseCSV(file);

    results.data.splice(0, 1);

    let contacts = results.data;

    // TODO: Validate before upload
    contacts = contacts.filter((contact) => !!contact[0]).map((contact) => {
      return {
        unitCode: contact[0].toString(),
        firstName: contact[1] || undefined,
        lastName: contact[2] || undefined,
        email: contact[3] || undefined,
        phone: contact[4] ? contact[4].toString() : undefined,
        gender: contact[5] || undefined,
        birthdate: contact[6] || undefined,
        contactType: contact[7] || undefined
      };
    });

    const params = {
      objects: contacts
    };

    try {
      await asyncCall('importBatch', params);
    } catch (error) {
      this.setState({ error });
    } finally {
      this.setState({ loading: false });
    }
  };

  return (
    <div>
      <label htmlFor="input">
        <Button
          as="div"
          icon
          color={ this.state[file.id] ? 'green' : undefined }
          className="fluid-mobile">
          <Icon name={ this.state[file.id] ? 'checkmark' : 'upload' }/>
        </Button>
      </label>

      <input
        id="input"
        name="input"
        type="file"
        accept=".csv"
        disabled={ loading }
        className="hidden"
        onChange={ this._importUsersBatch }/>
    </div>
  );
}

function parseCSV(file) {
  return new Promise(function(resolve, reject) {
    parse(file, {
      dynamicTyping: true,
      skipEmptyLines: true,
      error(error) {
        Sentry.captureException(error);

        reject(error);
      },
      complete(results) {
        resolve(results);
      }
    });
  });
}
```

Hope this helps