Skip to content

Instantly share code, notes, and snippets.

@voscausa
Last active September 27, 2019 23:23
Show Gist options
  • Save voscausa/cb64e7245a5c8b7a14bee60411f16e23 to your computer and use it in GitHub Desktop.
Save voscausa/cb64e7245a5c8b7a14bee60411f16e23 to your computer and use it in GitHub Desktop.
Batch upload a CSV file in a Firestore collection
<script>
// other imports
import Upload from "./upload/Upload.svelte";
// auth stuff
export let uid;
// use a dynamic component to be able to stop the component
let upload_component = Upload;
// we restart the component after the upload to clear the filename of the <input type="file" ..../>
let restart = false;
$: if (restart) {
// use a falsy to stop / destroy this component
upload_component = null;
// use a timer to queue a restart after the componets has stopped
setTimeout(() => {
console.log('Upload component restarts')
upload_component = Upload
}, 0);
console.log('Upload component restart queued');
restart = false;
};
</script>
<style>
input {
display: block;
}
</style>
<!-- dynamic Upload component to restart the component when finished-->
<svelte:component uid={uid} this={upload_component} bind:finished={restart}/>
import { db } from "./../firebase";
let uid;
let callback;
const MATCHSEP = new RegExp(/[^\r\n]+/gu); // split line
// const UNQUOTE = new RegExp(/^(").*(")$/gu); // remove start and end quotes
function batchRows(collectionName, batch) {
const idLen = 4;
const colNames = [];
let rowId = 0;
return (csvRow, rowNum) => {
const csvRowObj = {};
const rowItems = csvRow.split(';');
if (rowNum === 0) {
// get the colNames from the first csvRow
colNames.push(...rowItems);
} else {
// and the values from the other csvRows
for (const [i, v] of rowItems.entries()) {
csvRowObj[colNames[i]] = v;
}
if (!('id' in colNames)) {
// no id: use a fixed length numeric id with leading spaces
csvRowObj.id = (' '.repeat(idLen) + rowId).slice(-idLen);
rowId++;
}
// set an csvRow object in the uploads collection
let ref = db.collection(collectionName).doc(csvRowObj.id);
batch.set(ref, {
uid, created: Date.now(), ...csvRowObj
});
}
}
}
function fileSelect(event) {
//handle file select and create a batch of CSV rows to update the collection
const reader = new FileReader()
let files = event.target.files;
let f = files[0];
const collectionName = f.name;
reader.onload = (e) => {
const batch = db.batch();
const _batchRows = batchRows(collectionName, batch);
let contents = e.target.result;
let csvRows = contents.match(MATCHSEP);
csvRows.forEach(_batchRows);
batch.commit().then(() => {
console.log(`Batch for : ${collectionName} committed`);
callback(`Upload : ${collectionName} OK, CSV length : ${csvRows.length} rows`);
});
};
reader.onerror = (e) => {
callback(`Upload : ${collectionName} error: ${e.target.error.code}`);
};
if (f.type.match('application/vnd.ms-excel')) {
reader.readAsText(f);
} else {
callback(`Upload : ${collectionName} Invalid type: ${f.type} Must be a CSV file`)
}
}
export function uploadCsv(args) {
uid, callback = [...args];
return fileSelect;
}
<script>
import { onDestroy } from 'svelte';
import { uploadCsv } from './upload.js'
export let uid = null;
export let finished;
function uploadCsvCallback(result) {
console.log(result);
// restart is bound to component
finished = true;
};
onDestroy(() => {
console.log('Upload component destroyed');
});
</script>
<style>
.float-right {
float: right;
}
</style>
<input type="file" name="files" class="float-right" accept=".csv"
on:change={uploadCsv(uid, uploadCsvCallback)}/>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment