DojoFAQ

While a more recent advancement allows us use the HTML5 file API to retrieve contents from files, this approach is not universally supported in web browsers as yet. Instead, we will access data from user-uploaded CSV files using the following steps:

  • Upload a file to the server
  • Retrieve the file from the server
  • Load the data into an easy-to-use format

Uploading a file to the server

Traditional file uploads from within a form are only permitted when the entire form is submitted to the server and a new page is returned. Normally, file uploads are disallowed with XMLHttpRequests for security reasons.

To upload a CSV file, there are two options within Dojo: the FormData object or dojox/form/Uploader. The FormData approach is not supported prior to Internet Explorer version 10. Most of our users still need to support IE9, so this example will use the dojox/form/Uploader API. Like all Dijits, it can be instantiated programmatically or declaratively.

The Uploader widget is wrapped in a form element with its method set to post, its action set to /upload, and its enctype set to multipart/form-data. When a file is selected, the form will do a POST request to the url “/upload.”

We are instantiating the Uploader widget programmatically with a source node reference of “uploader”:

    var uploader = new Uploader({
        name: 'uploadedfile',
        type: 'file',
        label: 'Select CSV file',
        uploadOnSelect: true, // immediately upload file upon selected
        onComplete: onComplete
    }, 'uploader');

    uploader.startup();

Once a file is selected with the Uploader widget, the form will do a POST request to the specified url. The server side will need to save the file to a location where your application can reach it. This method works with almost any server-side approach for handling the file request. This particular example uses Express, a server-side JavaScript web framework for Node.js.

function upload (request, response) {
	var file,
		newPath;

	// save file to upload directory
	if (request.files) {
		file = request.files.uploadedfile;

		fs.readFile(file.path, function (error, data) {
			newPath = path.normalize(__dirname + '/../public/upload/' +
                        file.originalFilename);

			fs.writeFile(newPath, data, function (error) {
				if (error) {
					return next(error);
				}
				response.json({success: true, 
					message: 'file is successfully uploaded.', 
					filename: file.originalFilename
				});
			});
		});
	}
	else {
		response.status(400).json({success: false, message:'No file attached'});
	}
}

Once the file is successfully saved, the server will send a response with a status of 200 and return a JSON object with properties including the message and filename.

Retrieving the file from the server

In this example, dojo/request is used to fetch the file.

For the initial Uploader example, an onComplete handler is added to the Uploader widget upon instantiation. The function is called when the server returns a response.

function onComplete (response) {
	if (response.filename) {
		// request the uploaded csv file
		request('upload/' + response.filename).then(function (response) {
			// response = CSV string
			...
		});
	}
	else {
		console.log('Failed to upload file');
	}
}

The onComplete function uses dojo/request to retrieve the file previously uploaded and saved to the server. Once the request successfully retrieves the file, either dstore/Csv or dojo-smore/Csv can be used to place the response in an easy-to-use object store structure. The decision here will depend on which approach to object stores you wish to use within your application: either the newer dstore, or the existing dojo/store API.

dstore/Csv

The first step to use dstore/Csv is to mix the dstore/Memory with the dstore/Csv then initialize the object with the server response as the data property. You can access the CSV data with the initialized object data property. This data property will give you an array of objects with the key as the name and values as the value. Below, we will use dgrid/OnDemandGrid to display the CSV data. For example:

require([
	'dojo/_base/declare',
	'dojo/_base/array',
	'dojox/form/Uploader',
	'dstore/Memory',
	'dstore/Csv',
	'dgrid/OnDemandGrid', // dgrid 0.4+
	'dojo/request',
	'dojo/domReady!'
], function (
	declare,
	array,
	Uploader,
	Memory,
	Csv,
	OnDemandGrid,
	request
) { 
...
	// request the uploaded csv file
	request('upload/' + response.filename).then(function (response) {
		var csvData,
			// Mix Csv into Memory
			CsvMemory = declare([Memory, Csv]);

		csvData = new CsvMemory({
			data: response
			/* options: delimiter, newline... */
		});

		// [{key: value}, {key: value}, ...]
		console.log(csvData.fetchSync());
		// ["field1", "field2", ...]
		console.log(csvData.fieldNames);

		new OnDemandGrid({
			collection: csvData,
			// Columns can be taken directly from the collection
			columns: csvData.fieldNames
		}, 'grid'); // attach to a DOM id
	});
...
});

dojo-smore/Csv

When using dojo-smore/Csv, an object is initialized by passing in the CSV data string as the data property along with other options. Data is then accessed via the store’s data property, an array of objects with the key as the name and value as the value. Since we are displaying the data in a 0.4+ version of dgrid/OnDemandGridData, we need to use the dstore/legacy/StoreAdapter. If you are using dgrid 0.3.x with dojo/store, the adapter is not necessary. For example:

require([
	'dojo/_base/array',
	'dojox/form/Uploader',
	'dojo-smore/Csv',
	'dstore/legacy/StoreAdapter' // only for dgrid 0.4+
	'dgrid/OnDemandGrid', // dgrid 0.4+
	'dojo/request',
	'dojo/domReady!'
], function (
	array,
	Uploader,
	Csv,
	StoreAdapter,
	OnDemandGrid,
	request
) { 
...
	// request the uploaded csv file
	request('upload/' + response.filename).then(function (response) {
		var csvData;

		// use storeAdapter for dgrid
		csvData = new StoreAdapter({
			objectStore: new Csv({data: response})
		});

		// [{key: value}, {key: value}, ...]
		console.log(csvData.objectStore.data); 
		// ["field1", "field2", ...]
		console.log(csvData.objectStore.fieldNames);
		// convert the data to CSV
		console.log(csvData.objectStore.toCsv());

		new OnDemandGrid({
			collection: csvData,
			// Columns can be taken directly from the store
			columns: csvData.objectStore.fieldNames
		}, 'grid'); // attach to a DOM id
	});
...
});

Conclusion

Once data has been accessed, the data from the original CSV file is now saved on the server and available for use within the application.

Learning more

Join us in our Dojo workshops offered throughout the US, Canada, and Europe, or at your location to learn more about loading data via object stores and dstore. We also provide expert JavaScript and Dojo support and development services. Contact us for a free 30 minute consultation to discuss how we can help you efficiently manage data within your application.