#silverlight-3.0
#silverlight-3.0
Вопрос:
Хорошо — у меня есть служба WCF, которая считывает файл Excel из определенного местоположения и преобразует данные в объект. Что мне нужно, так это возможность разрешить пользователям моей программы загружать таблицу Excel в расположение файла, которое использует мой сервис.
Альтернативно, я мог бы передать загруженный лист Excel в службу напрямую.
Кто-нибудь может помочь с этим. Мой сервисный код:
public List<ImportFile> ImportExcelData(string FileName)
{
//string dataSource = Location FileName;
string dataSource = Location;
string conStr = "Provider=Microsoft.Jet.OLEDB.4.0;" "Data Source=" dataSource.ToString() ";Extended Properties=Excel 8.0;";
var con = new OleDbConnection(conStr);
con.Open();
var data = con.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, null);
var sheetName = data.Rows[0]["TABLE_NAME"].ToString();
OleDbCommand cmd = new OleDbCommand("SELECT * FROM [" sheetName "] WHERE Status = '4'", con);
OleDbDataAdapter oleda = new OleDbDataAdapter();
oleda.SelectCommand = cmd;
DataSet ds = new DataSet();
oleda.Fill(ds, "Employees");
DataTable dt = ds.Tables[0];
var _impFiles = new List<ImportFile>();
foreach (DataRow row in dt.Rows)
{
var _import = new ImportFile();
_import.PurchaseOrder = row[4].ToString();
try
{
var ord = row[8].ToString();
DateTime dati = Convert.ToDateTime(ord);
_import.ShipDate = dati;
}
catch (Exception)
{
_import.ShipDate = null;
}
ImportFile additionalData = new ImportFile();
additionalData = GetAdditionalData(_import.PurchaseOrder);
_import.NavOrderNo = additionalData.NavOrderNo;
_import.IsInstall = additionalData.IsInstall;
_import.SalesOrderId = additionalData.SalesOrderId;
_import.ActivityID = additionalData.ActivityID;
_import.Subject = additionalData.Subject ;
_import.IsMatched = (_import.ShipDate != null amp; _import.NavOrderNo != "" amp; _import.NavOrderNo != null amp; _import.ShipDate > DateTime.Parse("01/01/1999") ? true : false);
_import.UpdatedShipToField = false;
_import.UpdatedShipToFieldFailed = false;
_import.CreateNote = false;
_import.CreateNoteFailed = false;
_import.CompleteTask = false;
_import.CompleteTaskFailed = false;
_import.FullyCompleted = 0;
_import.NotCompleted = false;
_impFiles.Add(_import);
}
oleda.Dispose();
con.Close();
//File.Delete(dataSource);
return _impFiles;
}
Ответ №1:
Вы захотите изменить свой сервис, чтобы он принимал Stream
вместо имени файла, затем вы можете сохранить значение if off в файл (или проанализировать его непосредственно из Stream
, хотя я не знаю, как это сделать).
Затем в вашем приложении Silverlight вы могли бы сделать что-то вроде этого:
private void Button_Click(object sender, RoutedEventArgs ev)
{
var dialog = new OpenFileDialog();
dialog.Filter = "Excel Files (*.xls;*.xlsx;*.xlsm)|*.xls;*.xlsx;*.xlsm|All Files (*.*)|*.*";
if (dialog.ShowDialog() == true)
{
var fileStream = dialog.File.OpenRead();
var proxy = new WcfService();
proxy.ImportExcelDataCompleted = (s, e) =>
{
MessageBox.Show("Import Data is at e.Result");
// don't forget to close the stream
fileStream.Close();
};
proxy.ImportExcelDataAsync(fileStream);
}
}
Вы также могли бы заставить свою службу WCF принять byte[]
и сделать что-то вроде этого.
private void Button_Click(object sender, RoutedEventArgs ev)
{
var dialog = new OpenFileDialog();
dialog.Filter = "Excel Files (*.xls;*.xlsx;*.xlsm)|*.xls;*.xlsx;*.xlsm|All Files (*.*)|*.*";
if (dialog.ShowDialog() == true)
{
var length = dialog.File.Length;
var fileContents = new byte[length];
using (var fileStream = dialog.File.OpenRead())
{
if (length > Int32.MaxValue)
{
throw new Exception("Are you sure you want to load > 2GB into memory. There may be better options");
}
fileStream.Read(fileContents, 0, (int)length);
}
var proxy = new WcfService();
proxy.ImportExcelDataCompleted = (s, e) =>
{
MessageBox.Show("Import Data is at e.Result");
// no need to close any streams this way
};
proxy.ImportExcelDataAsync(fileContents);
}
}
Обновить
Ваш сервис может выглядеть следующим образом:
public List<ImportFile> ImportExcelData(Stream uploadedFile)
{
var tempFile = HttpContext.Current.Server.MapPath("~/uploadedFiles/" Path.GetRandomFileName());
try
{
using (var tempStream = File.OpenWrite(tempFile))
{
uploadedFile.CopyTo(tempStream);
}
//string dataSource = Location FileName;
string dataSource = tempFile;
string conStr = "Provider=Microsoft.Jet.OLEDB.4.0;" "Data Source=" dataSource.ToString()
";Extended Properties=Excel 8.0;";
var con = new OleDbConnection(conStr);
con.Open();
}
finally
{
if (File.Exists(tempFile))
File.Delete(tempFile);
}
}
Комментарии:
1. Не уверен, как это сделать в виде потока — Можно заставить его работать только с помощью описанного выше метода
2. Вам пришлось бы сохранить содержимое потока в локальном каталоге на сервере. Где-то у учетной записи службы WCF есть разрешение, возможно, в подпапке в корне веб-сайта или во временном каталоге.
3. Добавлено обновление, которое показывает, как перенести поток во временный файл и запустить его оттуда.
Ответ №2:
Спасибо, Бендевей, это было здорово. Пришлось немного изменить его — Мой сервис:
var tempFile = @"c:temp" Path.GetRandomFileName();
try
{
int length = 256;
int bytesRead = 0;
Byte[] buffer = new Byte[length];
// write the required bytes
using (FileStream fs = new FileStream(tempFile, FileMode.Create))
{
do
{
bytesRead = uploadedFile.Read(buffer, 0, length);
fs.Write(buffer, 0, bytesRead);
}
while (bytesRead == length);
}
uploadedFile.Dispose();
string conStr = "Provider=Microsoft.Jet.OLEDB.4.0;" "Data Source=" dataSource.ToString() ";Extended Properties=Excel 8.0;";
var con = new OleDbConnection(conStr);
Еще раз спасибо за вашу помощь
Комментарии:
1. к вашему сведению, 256 — это действительно маленькая длина буфера, есть на то причины? Я бы рекомендовал не менее 1024.