On the web, we must know the Internet media type of the image. Therefore, when coding your design model class, you MUST configure two properties – one for the image bytes (named Content, or something similar to that), and one for the internet media type string (named ContentType).

This topic is introduced in the following video:

.

Code example for today

In this Winter 2015 semester, your professor wrote a web app (and web service) to support his other course, DPS923, which is Mobile App Development for iOS.

The web app manages ‘sport’ and ‘venue’ data for this summer’s Pan Am Games in the Toronto area.

The Sport entity class has properties to hold a logo (which is a PNG), and a representative photo (which is a JPG). These properties are in the entity class, because a Sport can have only one logo.

The Venue entity class has properties to hold a representative photo (which is a PNG), and a map of the venue (which is a PDF). As above, these properties are in the entity class, for the same reasons.

The file upload user interface element must be coded next. Its name MUST match the property name of the ‘add xxxx’ view model class in the next step. Here is a typical file upload user interface element configuration:

For example, if you create an HTML img element, then set the value of its src attribute to the appropriate image.

.

General discussion of internet media type handling

The section above focused on one scenario. Real life is more complicated. This section includes a general discussion of internet media type handling in a web app.

Watch this video to get introduced to the problem:

.

Get content from a user

A section above covered this situation adequately.

For this discussion, the ‘save’ task can vary. We can save an internet media type (IMT) object:

In the file system

In the data store (i.e. the database)

Using a hybrid mixture of these two

The following discusses some of the problems and solutions for each.

Please note that the author does not favour one approach (or later ‘recipe’) for all situations.

.

Implications – file system storage

You will have to create a folder to hold the IMT objects, maybe called “assets” or something that fits with the web app’s problem domain.

You may be tempted to use an existing folder, but resist that temptation. You cannot use App_Data, because its contents are not publicly accessible. You should not use Content, because that has a specific use now, and may evolve in the future.

File names are important. Their length must be reasonable. They must be unique. To simplify coding and handling, the character set used for the file names should be neutral, especially for a web app that’s used worldwide.

An IMT object is logically associated with an entity object in your problem domain. How do you maintain that association? Carefully. It is likely that the IMT object itself cannot store information about the association, and the file name may not be enough do do the job. Therefore, the entity object must include a property with the IMT object’s file name.

How should a file system based IMT object be delivered to a browser user? Using a URL that maps to its file system location and file name? Or through some other ‘managed’ approach?

Beyond file names, extension names, and created/modified dates, the file system will not support metadata querying. For example, it will become difficult (and non-performant) to handle a query for all photos larger than 1000px wide and 800px tall, for example.

It also may be difficult or impossible to store descriptive metadata in the IMT object (depending upon its format).

In a web app that uses both a data store and the file system, the web app manager now must manage two separate storage locations, and ensure they’re backed up and secure.

.

Implications – data store (database-hosted)

In a database-hosted data store, the IMT object’s data is stored in a byte array (the C# type is Byte[]). This data type maps nicely to both the storage and delivery components involved. As a result, it’s on equal footing with the file system in this respect.

You MUST store the IMT object’s internet media type string. When uploaded, that metadata is available in the Content-Type of the request (and available in the uploaded object’s ContentType property).

An advantage of this approach is that IMT object metadata can be stored alongside its data, as additional properties in the class that holds the IMT object’s data. For example, you can store a “Title”, and a lengthy “Description”. Other properties that are relevant to the IMT object can be stored, and their values can come from the browser user, or by programmatically inspecting the IMT object. (For example, the pixel resolution of an image can be extracted from the data, without user intervention.)

Delivery of the IMT object is managed by your app. Often, a special-purpose controller accepts an identifier in the request URL, and delivers the content accordingly.

Backing up the app’s data is simple, because there’s a single location for all the app’s data.

A frequent and notable criticism of this approach is that an IMT object is transformed when stored, then again when retrieved, from the data store. For large IMT objects, this work can be considerable, and may hurt performance. Acknowledged.

.

Implications – hybrid approach, using both techniques

It is also possible to combine both techniques. In this approach, your app stores the IMT object’s data in the file system, and metadata in the data store.

The file system location can be a new folder, or a subfolder of App_Data. Often, the file name is a GUID.

This approach is often used in situations where the IMT object size is large, or where there’s a large number of IMT objects to manage.

.

Entity class design considerations when using the data store

As noted above, when using the data store, an entity class that includes an IMT object MUST have two properties:

A byte array, for the IMT object’s data

A string, for the internet media type

Beyond that, other properties can be added, if desired.

If the design of an entity class is intended to hold one single, distinct, and unique IMT object, then it’s acceptable to simply add these properties to the entity class.

In the ‘base’ view model classes, DO NOT include these properties. (Recall from today’s code example that an IMT object is delivered in a standard and correctly-configured HTTP response.)

If an entity object needs a collection of IMT objects, then create a separate entity class for the IMT object, and configure the to-one and to-many (or whatever) associations needed to meet the needs.

Finally, if the primary purpose of the app is to manage IMT objects, then the entity class(es) must be designed around the needs of the app, and the IMT entity classes become the central part of the design model.

.

Solution ‘recipes’

In summary, there are a number of possible approaches for handling internet media types. Your situation will determine the best one to use initially.

The list below briefly describes a number of solution ‘recipes’. All have a storage destination that’s determined by the recipe’s ingredients. The scenario in today’s code example is covered by the recipe number 3. In the future (if/when your professor has time), code examples for the other recipes will be completed.

Your web app can be deployed on any web server. When deployed, its files are copied to a location in the file system, and the app is served by the web server software. The file system configuration of a web server can vary, so you cannot predict the file system path of your app, and its App_Data folder.

Therefore, you will need to get the file system path programmatically. In a manager class (or another service class in your app), this code will get you a string with the file system path of a file named “NFLStats2014.csv”:

In addition, you may need to perform file system tasks, including read, write, delete, and find. Add this to your source code’s ‘using’ statements:

using System.IO;

.

Reading and processing a CSV file

The comma-separated values (CSV) data format has been used since the 1970s. It is a plain-text data format, which has the column (or field) names in the first row, comma-separated, and data values in the remaining rows.

.

We could read each line of text in the file, and do string parsing to extract the data values. However, we will not do that.

You can add it to your project by using the graphical or command-line NuGet Package Manager. (The console command is “install-package csvhelper”.)

.

CsvHelper features

Similar to AutoMapper, it can automatically map CSV column/field names to property names in a class. Non-matching items are ignored.

In addition, it supports customized mapping, when a column/field name does not match a property name. We will use this feature in our app.

We will use a ‘CsvReader’ object. It features two ways to ‘read’ the lines/rows in the CSV file: 1) All at once, and 2) In a loop.

The ‘all at once’ way can deliver the results to a List<TEntity>, which is very convenient for in-memory operations. The reader’s GetRecords<TEntity>() method is used (notice the pluralized name, GetRecords).

The ‘in a loop’ way enables you to process each line/row separately, which is very convenient for saving new objects to a data store, or for data transformation tasks. The reader’s GetRecord<Tentity>() method is used.

In any code module that uses CsvHelper, add “using CsvHelper;” to the list of ‘using’ statements.

Write a view model class that matches the design of the data in the CSV file. If the column/field and property names do not match, then use the information in the “Customized mappings” section below.

Decide how you must process the CSV file. Do you need to display the contents? Save the contents in a data store? Something else? Whatever the decision, prepare the data structures that you’ll need.

Next, open the file for reading, read the contents, process the contents, and close the file. The following code suggests the approach. For complete details, study the Manager.cs source code in the code example.

Data import – load data from an XLSX file

In this section, you will learn how to import data from a Microsoft Excel worksheet file (XLSX).

.

How to get the data file into your app

Similar to above, it can be done during development of your app, or after deployment. In both cases, store the file in the app’s App_Data folder.

.

Prepare to work with the file system

As above, you will need to work with the file system, and will use the same technique.

.

Reading and processing an XLSX file

The Office Open XML (XLSX) file format has been available since 2006, and is an open non-proprietary standard. It is the default data file format for Microsoft Excel.

.

Recently, Dietmar Schoder released a nice little library to enable quick and easy XLSX file reading, without the need to add large libraries and toolkits to your project. In addition, it does not impose any configuration changes at the web server.

.

Excel library features

It is not as automatic as AutoMapper or CsvHelper. However, it is still easy to use.

Reflecting the hierarchy of Excel and its data file format, it enables you to open a workbook (which is the XLSX file itself), and then work with a specific worksheet within the workbook. A worksheet has rows, and within a row, you will find cells (also known as columns).