среда, 25 августа 2010 г.

Импорт csv файлов в Silverlight

Еще одной интересной задачей, с которой мне пришлось сталкнуться – это импорт данных из csv файлов с использованием Silverlight.

Сразу стоит отметить, что, поскольку Silverlight – это урезанная клиентская технология, то в нем нет никаких полезных классов для импорта csv.

Так что импорт будет происходить по следующей схеме:

1. Пользователь выбирает csv файл на своем жестком диске

2. Файл загружается на сервер

3. На сервере происходит обработка данных

4. Данные загружаются в базу

5. Информация об ошибках и другие сообщения возвращаются клиенту.

Попробуем разобраться с каждым из этапов.

Выбор файла:

В Silverlight нет контрола вроде FileUpload, так что придется делать его самостоятельно. Такие контролы можно найти в интернете, но мы разработаем свою примитивную версию такого контрола, которая будет состоять из текстового поля и кнопки.

Вот так будет выглядеть код страницы:

<navigation:Page
x:Class="Sample.Import"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:riaControls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.DomainServices"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480"
Style="{StaticResource PageStyle}" >
<Grid x:Name="LayoutRoot">
<ScrollViewer x:Name="PageScrollViewer" Style="{StaticResource PageScrollViewerStyle}">
<StackPanel x:Name="ContentStackPanel" Style="{StaticResource ContentStackPanelStyle}">
<StackPanel Orientation="Horizontal" Margin="0,10,0,0">
<TextBlock Text="File:"/>
<TextBox Name="txtFileName" Width="150" Margin="30,0,0,0" IsEnabled="False" Background="Gray"/>
<Button Name="btnFileUpload" Content="Browse" Margin="0,0,0,0" Height="23" Click="btnFileUpload_Click" />
</StackPanel>
<Button HorizontalAlignment="Left" Content="Import" Height="23" Name="btnImport" Width="75"
Margin="0,10,0,0" Click="btnImport_Click" />
</StackPanel>
</ScrollViewer>
</Grid>
</navigation:Page>

При нажатии на кнопку Browse нужно открыть диалоговое окно выбора файла и сохранить выбор.

        private FileInfo _file;


 


        private void btnFileUpload_Click(object sender, System.Windows.RoutedEventArgs e)
         {
             OpenFileDialog openFile = new OpenFileDialog();
             openFile.Multiselect = false;
             openFile.Filter = "CSV files|*.csv";
             var result = openFile.ShowDialog();
             if (result.HasValue && result.Value == true)
             {
                 var file = openFile.File;
                 txtFileName.Text = file.Name;
                 _file = file;
             }
         }


Теперь, когда файл выбран, пользователь должен нажать на кпопку Import, чтобы импортировать файл. Мы же будем отправлять файл на сервер, а точнее в HttpHandler, который мы создадим далее.



Почти все операции, вроде поключения к серверу, создания стрима для запроса и т.д., являются асинхронными, из-за этого почти все наши методы будут вызываться асинхронно через callback.



Для подключения к серверу нам нужно правильным образом сформировать url. Тут я использую достаточно хитрый код, это сделано для того, чтобы избежать возможной ошибки для некоторых видов url строки.



Код подключения к серверу будет выглядеть так:



private void btnImport_Click(object sender, System.Windows.RoutedEventArgs e)

        {


if (_file != null)


            {


if (HtmlPage.Window.Confirm("Are you sure you want to import data?"))


                {


                    DoImport();


                }


            }


        }


private void DoImport()


        {


if (_file != null)


            {


                UploadFile(_file.Name, _file.OpenRead());


            }


        }


private void UploadFile(string fileName, Stream data)


        {


var uri = HtmlPage.Document.DocumentUri;


var pathElements = uri.AbsolutePath.Split('/');


var path = uri.AbsolutePath.Replace(pathElements[pathElements.Length - 1], string.Empty);


UriBuilder ub = new UriBuilder(uri.Scheme, uri.Host, uri.Port, path + "fileupload.ashx");


            ub.Query = string.Format("filename={0}", fileName);


var request = HttpWebRequest.Create(ub.Uri) as HttpWebRequest;


            request.Method = "POST";


            request.BeginGetRequestStream(WriteToStreamCallback, request);


        }



Файл будет загружаться на сервер при помощи достаточно обычного кода:



private void WriteToStreamCallback(IAsyncResult asynchronousResult)

        {


var request = asynchronousResult.AsyncState as HttpWebRequest;


Stream requestStream = request.EndGetRequestStream(asynchronousResult);


if (_file != null)


            {


                PushData(_file.OpenRead(), requestStream);


            }


        }


private void PushData(Stream input, Stream output)


        {


byte[] buffer = new byte[4096];


int bytesRead;


while ((bytesRead = input.Read(buffer, 0, buffer.Length)) != 0)


            {


                output.Write(buffer, 0, bytesRead);


            }


            input.Close();


            output.Close();


        }



Весь код загрузки файла.



        private void btnImport_Click(object sender, System.Windows.RoutedEventArgs e)
         {
             if (_file != null)
             {
                 if (HtmlPage.Window.Confirm("Are you sure you want to import data?"))
                 {
                     DoImport();
                 }
             }
         }
 
 
         private void DoImport()
         {
             if (_file != null)
             {
                 UploadFile(_file.Name, _file.OpenRead());
             }
         }
 
         private void UploadFile(string fileName, Stream data)
         {
             var uri = HtmlPage.Document.DocumentUri;
 
             var pathElements = uri.AbsolutePath.Split('/');
             var path = uri.AbsolutePath.Replace(pathElements[pathElements.Length - 1], string.Empty);
             UriBuilder ub = new UriBuilder(uri.Scheme, uri.Host, uri.Port, path + "fileupload.ashx");
             ub.Query = string.Format("filename={0}", fileName);
 
             var request = HttpWebRequest.Create(ub.Uri) as HttpWebRequest;
             request.Method = "POST";
             request.BeginGetRequestStream(WriteToStreamCallback, request);
 
         }
 
         private void WriteToStreamCallback(IAsyncResult asynchronousResult)
         {
             var request = asynchronousResult.AsyncState as HttpWebRequest;
             Stream requestStream = request.EndGetRequestStream(asynchronousResult);
 
             if (_file != null)
             {
                 PushData(_file.OpenRead(), requestStream);
             }
 
         }
 
         private void PushData(Stream input, Stream output)
         {
             byte[] buffer = new byte[4096];
             int bytesRead;
 
             while ((bytesRead = input.Read(buffer, 0, buffer.Length)) != 0)
             {
                 output.Write(buffer, 0, bytesRead);
             }
 
             input.Close();
             output.Close();
         }


Теперь нужно дать пользователю понять, когда файл действительно обработан сервером. Для этого нам нужно ожидать ответа от сервера, причем эта операция является асинхронной. Из-за этого, как раньше это было в WinForms, нельзя никаким образом изменять пользовательский интерфейс из того же потока, в котором мы получаем ответ от сервера.



        private void WriteToStreamCallback(IAsyncResult asynchronousResult)
         {
             var request = asynchronousResult.AsyncState as HttpWebRequest;
             Stream requestStream = request.EndGetRequestStream(asynchronousResult);
 
             if (_file != null)
             {
                 PushData(_file.OpenRead(), requestStream);
             }
 

            request.BeginGetResponse(ReadFromStreamCallback, request);

        }


        private void ReadFromStreamCallback(IAsyncResult asynchronousResult)
         {
             HttpWebRequest webRequest = (HttpWebRequest)asynchronousResult.AsyncState;
             HttpWebResponse webResponse = (HttpWebResponse)webRequest.EndGetResponse(asynchronousResult);
 
             this.Dispatcher.BeginInvoke(new UpdateUI(ShowMessage));
         }
 
         public void ShowMessage()
         {
             HtmlPage.Window.Alert("Complete!");
         }


Весь код сейчас будет выглядеть вот так:



namespace Sample

{
     using System.Windows.Controls;
     using System.Windows.Navigation;
     using System;
     using System.Collections.Generic;
     using System.Windows.Browser;
     using System.Net;
     using System.IO;
     using System.Xml.Serialization;
     using System.Runtime.Serialization;
     /// <summary>
     /// <see cref="Page"/> class to present information about the current application.
     /// </summary>
     public partial class Import : Page
     {
         private FileInfo _file;
         public delegate void UpdateUI();
 
         /// <summary>
         /// Creates a new instance of the <see cref="About"/> class.
         /// </summary>
         public Import()
         {
             InitializeComponent();
         }
 
         /// <summary>
         /// Executes when the user navigates to this page.
         /// </summary>
         protected override void OnNavigatedTo(NavigationEventArgs e)
         {
         }
 
         private void btnFileUpload_Click(object sender, System.Windows.RoutedEventArgs e)
         {
             OpenFileDialog openFile = new OpenFileDialog();
             openFile.Multiselect = false;
             openFile.Filter = "CSV files|*.csv";
             var result = openFile.ShowDialog();
             if (result.HasValue && result.Value == true)
             {
                 var file = openFile.File;
                 txtFileName.Text = file.Name;
                 _file = file;
             }
         }
 
 
         private void btnImport_Click(object sender, System.Windows.RoutedEventArgs e)
         {
             if (_file != null)
             {
                 if (HtmlPage.Window.Confirm("Are you sure you want to import data?"))
                 {
                     DoImport();
                 }
             }
         }
 
 
         private void DoImport()
         {
             if (_file != null)
             {
                 UploadFile(_file.Name, _file.OpenRead());
             }
         }
 
         private void UploadFile(string fileName, Stream data)
         {
             var uri = HtmlPage.Document.DocumentUri;
 
             var pathElements = uri.AbsolutePath.Split('/');
             var path = uri.AbsolutePath.Replace(pathElements[pathElements.Length - 1], string.Empty);
             UriBuilder ub = new UriBuilder(uri.Scheme, uri.Host, uri.Port, path + "fileupload.ashx");
             ub.Query = string.Format("filename={0}", fileName);
 
             var request = HttpWebRequest.Create(ub.Uri) as HttpWebRequest;
             request.Method = "POST";
             request.BeginGetRequestStream(WriteToStreamCallback, request);
 
         }
 
         private void WriteToStreamCallback(IAsyncResult asynchronousResult)
         {
             var request = asynchronousResult.AsyncState as HttpWebRequest;
             Stream requestStream = request.EndGetRequestStream(asynchronousResult);
 
             if (_file != null)
             {
                 PushData(_file.OpenRead(), requestStream);
             }
 
             request.BeginGetResponse(ReadFromStreamCallback, request);
         }
 
         private void PushData(Stream input, Stream output)
         {
             byte[] buffer = new byte[4096];
             int bytesRead;
 
             while ((bytesRead = input.Read(buffer, 0, buffer.Length)) != 0)
             {
                 output.Write(buffer, 0, bytesRead);
             }
 
             input.Close();
             output.Close();
         }
 
         private void ReadFromStreamCallback(IAsyncResult asynchronousResult)
         {
             HttpWebRequest webRequest = (HttpWebRequest)asynchronousResult.AsyncState;
             HttpWebResponse webResponse = (HttpWebResponse)webRequest.EndGetResponse(asynchronousResult);
 
             this.Dispatcher.BeginInvoke(new UpdateUI(ShowMessage));
         }
 
         public void ShowMessage()
         {
             HtmlPage.Window.Alert("Complete!");
         }
     }

}

Комментариев нет:

Отправить комментарий