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>
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;
}
}
{
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");
}
}
}
}
}
}
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())
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);
_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();
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
Thank you for a very good article :-)
ReplyDeleteSaved my day......