📜 ⬆️ ⬇️

Silverlight File Upload Progress

Somehow there was a task before me, to organize File Upload Progress for the ASP.NET platform. Many technologies and solutions were dug up, but it was not possible to find a simple one. It was written by HttpModule, which coped with its tasks, but it was quite difficult to use it.

And then I thought: how does Silverlight cope with this task? He began to actively search for this topic and saw that there were not so many ready-made solutions (more precisely, I did not find them at all).

In this topic, I give my example of creating the progress of downloading a file to the server, using SilverLight 2b2 technology.
')


1. Creating a Silverlight project

Let's start by creating a new project like Silverlight Application (in C #). The studio will offer to choose how our application will be “hosted”: either an ASP.NET website, or a generated HTML page. Most of all, the first option will suit us, and we choose it.


Now we have a "solution", which contains two projects. This is an ASP.NET website and Silverlight application.

2. Add and configure WCF service

To save files on the server, we will use the Windows Communication Foundation (WCF) service. I ask you to treat the service code with understanding, I wanted to make it extremely simple and short, so we have absolute paths, disregard of String.Format and possibly other blots (we don’t pay attention to quotes).
public class Receiver: IReceiver
{
public void SaveFile ( String filename, Int32 dataLength, Byte [] data)
{

FileStream fs = File .Open ( @ "C: \ Temp \" + filename, FileMode.Append);
fs.write (data, 0, dataLength);
fs.Close ();
}

public void DeleteIfExists (String filename)
{
if (File.Exists (@ " C: \ Temp \ " + filename)) File.Delete (@ " C: \ Temp \" + filename);
}
}

The code includes two methods: saving the file and deleting the file, if there is one. Here you should pay attention to the fact that the file is saved in chunks. SaveFile takes the file name, the length of the array and, in fact, an array of bytes. Received content is saved to a file opened with the Append mode. Before each file sending, the function of file deletion is called so that there is no situation when a new file is added to the existing one.

After creating a WCF service, you need to change the type of "binding", which is specified in the Web.config file. Find the string "wsHttpBinding" and change it to "basicHttpBinding". The result is the following code:
< services >
< service behaviorConfiguration = "ReceiverBehavior" name = "Receiver" >
< endpoint address = "" binding = "basicHttpBinding" contract = "IReceiver" >
< identity >
< dns value = "localhost" />
</ identity >
</ endpoint >
< endpoint address = "mex" binding = "mexHttpBinding" contract = "IMetadataExchange" />
</ service >
</ services >
The reason for this is that Silverlight applications can work only with basicHttpBinding "binding" (for now or so it will always be - not known).

3. Connecting WCF service

To use the created service in the Silverlight application, you need to add a reference to the service (Add Service Reference ...). In the dialog box for adding a service, by clicking the Discover button, you can select any service of the current “solution”. Choose our service: Receiver, let's call it ReceiverService.


With the service for now, everything, now we can use it in the Silverlight application.

4. XAML code

Open the Page.xaml file and enter the following XAML code there:
< UserControl x: Class = “UploadProgress.Page”
xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns: x = "http://schemas.microsoft.com/winfx/2006/xaml"
Width = "400" Height = "30" >
< StackPanel Orientation = "Horizontal" >
< Button x: Name = "btnBrowse" Content = "Browse"
Width = "100" Height = "25" Click = "OnBrowseClick" />
< TextBlock x: Name = "lblProgress"
Text = "Please select file ..." Margin = "5" />
</ StackPanel >
</ UserControl >
This code describes the StackPanel, which contains a button and a block of text. At the button, the OnBrowseClick handler is hung on the Click event.

Actually, this is all we need for the interface. Now we can delve into the code that will be involved in sending the file.

5. Sending a file

First we describe the global class variables that we need when sending the file:
private ReceiverClient _receiver; // our WCF service created earlier
private String _fileName; // name of the file being uploaded
private Stream _fileData; // file stream of the uploaded file
private Int64 _dataLength; // length of transmitted data
private Int64 _dataSent; // length of sent data
Now we describe the event handlers. When you click on a button, the OnBrowseClick handler is called first.
private void OnBrowseClick (Object sender, RoutedEventArgs e)
{
var dlg = new OpenFileDialog (); // File Selection Dialog
dlg.Multiselect = false ; // Disable multiple file selection
dlg.Filter = "All Files | *. *" ; // Set Filter On Files

if ((Boolean) dlg.ShowDialog ()) // show the file selection dialog
{
_fileName = dlg.SelectedFile.Name; // name of the file being uploaded
_fileData = dlg.SelectedFile.OpenRead (); // file stream

_dataLength = _fileData.Length; // length of transmitted data
_dataSent = 0; // length of the transferred data

_receiver = new ReceiverClient (); // Receiver (web-service)

// file deletion event
_receiver.DeleteIfExistsCompleted + = OnDeleteIfExistsCompleted;

// event of saving a chunk of data
_receiver.SaveFileCompleted + = OnSaveFileCompleted;

// call the file deletion function
// in case the file with the same name already exists
// as a result, the handler will be called
// OnDeleteIfExistsCompleted
_receiver.DeleteIfExistsAsync (_fileName);
}
}
As a result of executing the delete function, the OnDeleteIfExistsCompleted handler is called:
private void OnDeleteIfExistsCompleted (Object sender, AsyncCompletedEventArgs e)
{
OnSaveFileCompleted (sender, e);
}
This function does nothing except what the OnSaveFileCompleted handler calls. The OnSaveFileCompleted handler reads a portion of the data from the file stream and calls the send function SaveFileAsync, which causes the same OnSaveFileCompleted processor to be called. Recursion continues until the entire file is transferred, after which the OnUploadCompleted method is called.
private void OnSaveFileCompleted (Object sender, AsyncCompletedEventArgs e)
{
Byte [] buffer = new Byte [4 * 4096];
Int32 bytesRead = _fileData.Read (buffer, 0, buffer.Length);

if (bytesRead! = 0)
{
_receiver.SaveFileAsync (_fileName, bytesRead, buffer);
_dataSent + = bytesRead;

// notify about the next load of data
OnProgressChanged ();
}
else
{
// all data loaded
OnUploadCompleted ();
}
}
The length is 4 * 4096 bytes - chosen by me arbitrarily. Perhaps there is a more optimal length, but so far. And the last.
private void OnProgressChanged ()
{
// display the current progress of the file download
lblProgress. Text = String. Format ( "{0} / {1}" , _dataSent, _dataLength);
}

private void OnUploadCompleted ()
{
// inform about the end of the download
lblProgress. Text = "Complete!" ;
}
That's all. Download sources for VS2008 SP1 and Silverlight b2b here .

Ps. If necessary, you can get rid of the Silverlight interface, make all calls from JavaScript. If someone is interested, I will write about it next time.

Source: https://habr.com/ru/post/38296/


All Articles