Monday, August 22, 2016

Web API 2: C# Upload File using HTML, CURL and .NET Clients

This posting presents the code used on the server-side to receive a file from a web client (Web API 2, C#). Also shown will be  three clients: HTML, C#/.NET and CURL. As I've said previous, CURL is my preferred HTTP client as it is platform agnostic. An IOS developer should be able to reverse engineer any web service example written in CURL.

HTML Client

Before presenting the server code used to upload a file, consider the following web page, detault.html, which allows a file to be uploaded:



The HTML code for the page is as follows:

<!DOCTYPE html>
<html>
<head>
    <title>Test file upload</title>
    <meta charset="utf-8" />
</head>
<body>
    <form action="http://localhost:4323/API/Files"
          enctype="multipart/form-data" method="post">
        <p>
            Select a file to upload:<br>
            <input type="file" name="datafile" size="50">
        </p>
        <div>
            <input type="Submit" value="Send">
        </div>
    </form>
</body>
</html>

The page contains a form which will perform an HTTP POST to the development URL:
    http://localhost:4323/API/Files

The page contains an input control of type file (named "datafile") which is clearly visible in the screenshot. The server side code needs to receive an HTTP POST and process the file accordingly.

Web Service

A controller named Files was was implemented in the web service which means the URL used by the HTML client makes sense as the web service URL contains API\Files:
    http://localhost:4323/API/Files

The code is as follows where the method implemented is Post() corresponding POST protocol used by the HTML client:

public class FilesController : ApiController
{
    private const string _fileUploadName = "datafile";

    public int Post()
    {
        string serverFolder = HostingEnvironment.MapPath(
                                      "~/Uploads/");
        HttpFileCollection files =       
             HttpContext.Current.Request.Files;
        HttpPostedFile postedFile = files[_fileUploadName];

        if (postedFile == null)
        {
            return 0;
        }

        string destinationFilename = Path.Combine(
                    serverFolder, 
                    Path.GetFileName(postedFile.FileName));

        if (File.Exists(destinationFilename))
        {
            return 0;
        }

        postedFile.SaveAs(destinationFilename);

        return 1;
    }
}


The following code creates path to the Uploads folder such that is resides under the web services folder:
    string folder = HostingEnvironment.MapPath("~/Uploads/");

The request, System.Web.HttpContext.Current.Request, HttpRequest contains a Files collection corresponding to the file or files uploaded:
    HttpFileCollection files = 
        System.Web.HttpContext.Current.Request.Files;

The file is stored in the Files collection with key "datafile":
    private const string _fileUploadName = "datafile";

    HttpPostedFile postedFile = files[_fileUploadName];

The HttpPostedFile class contains a SaveAs method that allows the files to be saved to physical media on the server:
    postedFile.SaveAs(destinationFilename);

Curl Client

The CURL code to upload a file is as follows:
curl -X POST -Fdatafile=@FileToUpload.txt http://localhost:4323/API/Files

The -X POST command-line parameters means CURL will execute a POST message. The -F is the form data where the input is named "datafile" and is associated with a file, @FileToUpload.Txt. The @ prefix indicates a file. The file resides in the same folders as the CURL script. The FileToUpload.txt is ultimately what gets uploaded.

C# Client

The C# client that uploads a file is follows:

private const string _fileToUpload = "FileToUpload.txt";

private const string _fileUploadName = "datafile";

private const string _uploadEndPoint = @"http://localhost:4323/API/Files";

static void Main(string[] args)
{
    using (HttpClient httpClient = new HttpClient())
    {
        using (MultipartFormDataContent content = 
                         new MultipartFormDataContent())
        {
            using (FileStream stream = File.Open(
                  _fileToUpload, FileMode.Open, FileAccess.Read))
            {
                using (StreamContent streamConent = 
                                    new StreamContent(stream))
                {
                    Task taskUpload;

                    content.Add(
                      streamConent, _fileUploadName, _fileToUpload);
                    taskUpload = httpClient.PostAsync(
                          _uploadEndPoint, content);
                    taskUpload.Wait();
                    if (taskUpload.Status == 
                                TaskStatus.RanToCompletion)
                    {
                        Console.WriteLine("File uploaded");
                    }

                    else
                    {
                        Console.WriteLine("File uploaded");
                    }
                }
            }
        }
    }
}

The HTTP client class (the class that performs the actualy post) is called (appropriately) HttpClient:
    HttpClient httpClient

The variable corresponding to the web forms's controls is MultipartFormDataContent (the form content) which is declared as follows:
    using (MultipartFormDataContent content = 
                         new MultipartFormDataContent())

The physical file is opened as a FileStream and that FileStream is associated with a StreamContent instance used to associated the file as part of the Form content submitted:

    using (FileStream stream = File.Open(
                  _fileToUpload, FileMode.Open, FileAccess.Read))
    {
        using (StreamContent streamConent = 
                                    new StreamContent(stream))

When the stream content is associated with the form the Add method is invoked. The second parameter of the Add method is passed the value _fileUploadName. The data associatd with _fileUploadName is filedata which was the name of the file input control in the original HTML client demonstrated:
    content.Add(streamConent, _fileUploadName, _fileToUpload);

The HTTP POST supported by an asynchronous method within HttpClient:
    taskUpload = httpClient.PostAsync(_uploadEndPoint, content);
    taskUpload.Wait();

Source Code

The source code for this project can be found in GitHub at https://github.com/softwarepronto/Blog:
  • C# Web Service: DemoWebService01
  • HTML Upload Gage: DemoWebClient01\default.html
  • C# file upload client: Client project
  • Curl file upload script: Tests\Local\JSON\PostAFile.bat

1 comment :

  1. Thank you for a very good article :-)

    Saved my day......

    ReplyDelete