Мы остановились на том, что создали код загрузки данных на сервер. Теперь нужно подготовить серверный обработчик, который будет сохранять файл, обрабатывать его и возращать какой-то ответ Silverlight приложению.
Создаем новый HttpHandler, называем его fileupload.ashx.
В нем напишем код для сохранения файла на сервер.
public class fileupload : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
try
{
string filename = context.Request.QueryString["filename"].ToString();
string serverFolderName = context.Server.MapPath("~/App_Data/");
string serverFileName = Guid.NewGuid().ToString().Replace('-', 'x') + ".csv";// +filename;
using (FileStream fs = File.Create(serverFolderName + serverFileName))
{
SaveFile(context.Request.InputStream, fs);
}
LoadFileToDatabase(context.Server.MapPath("~/App_Data/"), serverFileName);
context.Response.Write("Success");
}
catch (Exception ex)
{
context.Response.Write("Error while import");
}
}
private void LoadFileToDatabase(string folderName, string fileName)
{
}
private void SaveFile(Stream stream, FileStream fs)
{
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0)
{
fs.Write(buffer, 0, bytesRead);
}
}
public bool IsReusable
{
get
{
return false;
}
}
}
Этот код просто сохраняет файл со случайным именем в папку App_Data (кстати, нужно потом не забыть его стереть).
Нам еще нужно не забыть проверить права пользователя, может ли он осуществлять импорт? Это достаточно стандартный сценарий. Самое просто – это передать информацию о пользователе через строку запроса.
Теперь вспомним, что Silverlight – это клиентская технология, так что пользователь может легко обмануть сервер, и подсунуть ему неправильную информацию о себе (можно это сделать и более простым способом, хотя сейчас это не слишком важно).
Так что воспользуемся средствами Silverlight и Ria Services. Как мы помним, у нас есть специальный AuthenticationService для этих целей.
Модифицируем наш код.
Сначала нужно проверить, что пользователь аутентифицирован, а затем предположим, что у нас есть некоторая роль Администратора, и только Администратор может делать импорт файлов.
Предположим, что
using System;
using System.Web;
using System.IO;
using System.Data.OleDb;
using System.Data;
using System.Xml.Serialization;
using System.Runtime.Serialization;
namespace Sample.Web
{
public class fileupload : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
try
{
var serv = new Sample.Web.AuthenticationService();
var user = serv.GetUser();
if (user != null && user.IsAuthenticated)
{
string filename = context.Request.QueryString["filename"].ToString();
// try check rights
if (user.IsAdmin)
{
string serverFolderName = context.Server.MapPath("~/App_Data/");
string serverFileName = Guid.NewGuid().ToString().Replace('-', 'x') + ".csv";// +filename;
using (FileStream fs = File.Create(serverFolderName + serverFileName))
{
SaveFile(context.Request.InputStream, fs);
}
LoadFileToDatabase(context.Server.MapPath("~/App_Data/"), serverFileName);
context.Response.Write("Success");
}
else
{
throw new Exception("Access denied. No permissions");
}
}
else
{
throw new Exception("Access denied. User not authenticated");
}
}
catch (Exception ex)
{
context.Response.Write("Error while import");
}
}
private void LoadFileToDatabase(string folderName, string fileName)
{
}
private void SaveFile(Stream stream, FileStream fs)
{
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0)
{
fs.Write(buffer, 0, bytesRead);
}
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
Теперь нам нужно разобрать csv файл с данными. Для этого я использую Microsoft.Jet драйвер. Стоит учитывать некоторый особенности работы с Microsoft.Jet.
Microsoft.Jet умеет читать текстовые и csv файлы.
CSV файлы могут включать в себя заколовки колонок, также нужно указать в строке подключения HDR=Yes, если в первой строке содержатся заголовки.
Для csv файлов можно задать символ для разделителя. Можно задать этот символ в строке подключения, например так FTM=Delimited(,). Правда это не работает, во всяком случае, у меня.
Для того, чтобы разделительно был правильно установлен я меняю следующий ключ реестра:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Jet\4.0\Engines\Text\Format
Пожалуй, наиболее интересным фактом является то, что Microsoft.Jet не должен работать на 64-битных операционных системах (забавно).
Далее, оказывается, что Microsoft.Jet все же работает на 64-битных операционных системах в режиме Wow6432. Для этого нужно сделать какие-то хитрые операции по импорту dll в реестр, затем найти в реесте следующий ключ и заменить его на нужный.
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Jet\4.0\Engines\Text\Format
Код импорта будем выглядеть так:
private void LoadFileToDatabase(string folderName, string fileName)
{
// change registry key at: (set to ,)
// HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Jet\4.0\Engines\Text\Format
using (OleDbConnection connection = new OleDbConnection(string.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Extended Properties=\"text;HDR=Yes;FMT=Delimited(,)\"", folderName)))
{
connection.Open();
DataSet ds = new DataSet();
using (OleDbDataAdapter adapter = new OleDbDataAdapter(string.Format("select * from {0}", fileName), connection))
{
adapter.Fill(ds);
}
DataTable table = ds.Tables[0];
DoImport(table);
}
}
Весь, получившийся код обработчика:
using System;
using System.Web;
using System.IO;
using System.Data.OleDb;
using System.Data;
using System.Xml.Serialization;
using System.Runtime.Serialization;
namespace Sample.Web
{
public class fileupload : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
try
{
var serv = new Sample.Web.AuthenticationService();
var user = serv.GetUser();
if (user != null && user.IsAuthenticated)
{
string filename = context.Request.QueryString["filename"].ToString();
// try check rights
if (user.IsAdmin)
{
string serverFolderName = context.Server.MapPath("~/App_Data/");
string serverFileName = Guid.NewGuid().ToString().Replace('-', 'x') + ".csv";// +filename;
using (FileStream fs = File.Create(serverFolderName + serverFileName))
{
SaveFile(context.Request.InputStream, fs);
}
LoadFileToDatabase(context.Server.MapPath("~/App_Data/"), serverFileName); context.Response.Write("Success");
}
else
{
throw new Exception("Access denied. No permissions");
}
}
else
{
throw new Exception("Access denied. User not authenticated");
}
}
catch (Exception ex)
{
context.Response.Write("Error while import");
}
}
private void LoadFileToDatabase(string folderName, string fileName)
{
// change registry key at: (set to ,)
// HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Jet\4.0\Engines\Text\Format
using (OleDbConnection connection = new OleDbConnection(string.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Extended Properties=\"text;HDR=Yes;FMT=Delimited(,)\"", folderName)))
{
connection.Open();
DataSet ds = new DataSet();
using (OleDbDataAdapter adapter = new OleDbDataAdapter(string.Format("select * from {0}", fileName), connection))
{
adapter.Fill(ds);
}
DataTable table = ds.Tables[0];
DoImport(table);
}
}
private void SaveFile(Stream stream, FileStream fs)
{
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0)
{
fs.Write(buffer, 0, bytesRead);
}
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
Теперь можно вернуться к коду Silverlight страницы и сделать вот что: в зависимости в результата импорта выводить разные сообщения. По сути нужно просто выводить на экран то, что получено в респонсе.
Делается это примерно так:
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(string msg);
/// <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(false);
}
}
}
private void DoImport(bool ignoreWarnings)
{
if (_file != null)
{
UploadFile(_file.Name, _file.OpenRead(), ignoreWarnings);
}
}
private void UploadFile(string fileName, Stream data, bool ignoreWarnings)
{
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), webResponse.GetResponseStream().ToString());
}
public void ShowMessage(string msg)
{
HtmlPage.Window.Alert(msg);
}
}
}
Комментариев нет:
Отправить комментарий