Upload Download file на C# ASP.NET
Надо организовать некий HTTP-сервер на который один клиент закачает файл, а другие смогут его забрать. Плюс ведется лог, кто (ip) когда закачал и кто когда забрал.
Поверх ещё много разной логики, но это основа.
Так вот думаю, свой сервер фигачить (HTTPListener) или использовать готовый(IIS) ?
Если не сложно, покажите простой пример, как будет выглядеть C# ASP код, принимающий и сохраняющий файл и код его отдающий.
На основе этого буду думать, вникать в ASP или нет.
Спасибо.
показано как принять файл, сохранить его в БД даже и потом из БД извлеч и показать. (сделано очень хитро. Response.OutputStream.Write((byte[])dbRead["FileData"], 0, (int)dbRead["FileSize"]); //ты обратишься к какой-нибудь http://localhost/page.aspx, а начнется загрузка файла)
собственно там на примере картинки, но файл может быть каким угодно, и не обязательно сохранять в базу, можно и на файловую систему, а потом в asp на событие нажатия на какую-то кнопку перенаправлять пользователя к файлу, одновременно запоминая его айпи, время и т.д.
У IIS'а есть неприятная особенность - ограничение на размер закачиваемого файла. Это значение задается в его метабазе.
Код разметки Default.aspx:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:FileUpload ID="FileUpload1" runat="server" onLoad="FileUpload1_Load" />
<asp:Button ID="Button1" runat="server" Text="Upload!" /><br />
<asp:Repeater ID="Repeater1" runat="server">
<ItemTemplate>
<asp:HyperLink ID="url1" runat="server" Text='<%# Eval("Name") %>' NavigateUrl='<%# "~/Uploads/" + Eval("Name") %>' />
</ItemTemplate>
<SeparatorTemplate>
<br />
</SeparatorTemplate>
</asp:Repeater>
</form>
</body>
</html>
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.IO;
public partial class _Default : System.Web.UI.Page {
private readonly string uploads_dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Uploads");
protected void Page_Load(object sender, EventArgs e) {
}
protected void FileUpload1_Load(object sender, EventArgs e) {
if (FileUpload1.HasFile) {
HttpPostedFile user_file = FileUpload1.PostedFile;
DirectoryInfo uploads = new DirectoryInfo(uploads_dir);
if (!uploads.Exists)
uploads.Create();
string user_file_server_name = Path.Combine(uploads.FullName, Path.GetFileName(user_file.FileName));
using (FileStream stream = new FileStream(user_file_server_name, FileMode.Create, FileAccess.Write, FileShare.None)) {
byte[] buffer = new byte[4096];
int bytes_read = 0;
do {
stream.Write(buffer, 0, bytes_read);
bytes_read = user_file.InputStream.Read(buffer, 0, buffer.Length);
} while (bytes_read > 0);
}
}
}
protected void Page_PreRender(object sender, EventArgs e) {
DirectoryInfo uploads = new DirectoryInfo(uploads_dir);
if (!uploads.Exists)
uploads.Create();
Repeater1.DataSource = uploads.GetFiles();
Repeater1.DataBind();
}
}
Начал вникать в ASP.NET и появился вопрос: а то ли это, что мне надо? :)
Вот, хочу посоветоваться с Вами.
Стоит следущая задача.
Есть ряд приложений-клиентов, которые будут закачивать файлы (Download) автоматически по некоторому событию.
Есть "консоль администратора", с которой будут передаваться эти файлы (Upload) на сервер, для дальнейшей передачи клиентам.
Есть сервер, который принимает от администратора файлы и раздает их приложениям-клиентам, регистрируя кому чего отдал.
Т.е. по-существу это такая система автоматических апдейтов клиентских машин.
Все происходит по HTTP, но без использования браузеров, т.к. все автоматически. Т.е. мне не нужна генерация человеческих web-страниц.
Писать свой сервак (по-сути файлообменник) очень не хочется.
Поможет мне здесь ASP или я копаю не в ту сторону?
Может есть решение по-проще?
Тогда наверно не имеет смысла делать классические страницы, лучше воспользоваться Http-обработчиками (реализациями интерфейса IHttpHandler или IHttpAsyncHandler). Веб-сервер будет напрямую передавать им запрос.
Думаю, для такой задачи лучше сразу пользоваться асинхронными обработчиками, так как синхронные могут быстро заполнить все рабочие потоки на длительных файловых операциях.
З.Ы. Вообще, у меня как-то были мысли создать похожую систему обновления кое каких своих программ. К сожалению готовых полных (сервер, админка и клиентский модуль) решений не нашел, хотя может быть что-то такое есть в CruiseControl.NET (но туда нужно все проекты пересаживать).
Думаю, для такой задачи лучше сразу пользоваться асинхронными обработчиками, так как синхронные могут быстро заполнить все рабочие потоки на длительных файловых операциях.
З.Ы. Вообще, у меня как-то были мысли создать похожую систему обновления кое каких своих программ. К сожалению готовых полных (сервер, админка и клиентский модуль) решений не нашел, хотя может быть что-то такое есть в CruiseControl.NET (но туда нужно все проекты пересаживать).
Почитал на счет IHttpAsyncHandler, в частности, вот это. Прикинул, что upload будет делать одна админская консоль, а download буду делать напрямую (не через обработчик, а заданием пути до файла).
И остановился все же на IHttpHandler:
using System.Web;
using System.IO;
using System.Net;
namespace WebApplication2
{
public class Handler1 : IHttpHandler
{
private readonly string uploads_dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Uploads");
public void ProcessRequest(HttpContext context)
{
try
{
DirectoryInfo uploads = new DirectoryInfo(uploads_dir);
if (!uploads.Exists)
{
uploads.Create();
}
for(int i=0; i < context.Request.Files.Count; i++)
{
HttpPostedFile file = context.Request.Files;
if (file.ContentLength > 0)
{
file.SaveAs( Path.Combine(uploads.FullName, Path.GetFileName(file.FileName)) );
}
}
context.Response.Write("OK");
}
catch (System.Exception ex)
{
context.Response.Write("Trouble: " + ex.Message);
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
}
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
Вопрос снят:
context.Request.UserHostAddress;
Ссылка интересная спасибо. :)
Я бы вот так написал:
using System.Web;
using System.IO;
using System.Net;
namespace WebApplication2
{
public class Handler1 : IHttpHandler
{
private[COLOR=Red] [COLOR=Black]static [/COLOR][/COLOR]readonly string uploads_dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Uploads");
public void ProcessRequest(HttpContext context)
{
try
{
DirectoryInfo uploads = new DirectoryInfo(uploads_dir);
if (!uploads.Exists)
{
uploads.Create();
}
foreach(HttpPostedFile file in context.Request.Files)
{
if (file.ContentLength > 0)
{
file.SaveAs( Path.Combine(uploads.FullName, Path.GetFileName(file.FileName)) );
}
}
context.Response.Write("OK");
}
catch (System.Exception ex)
{
context.Response.Write("Trouble: " + ex.Message);
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
}
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
Если расширение файла у хэндлера отлично от ashx, с IIS'ом одна тонкость будет (во всяком случае с 6м): нужно добавлять ASP.NET ISAPI фильтр на это расширение и отключить проверку существования файлов, иначе http-обработчики не будут вызываться (подразумевается что файла TestHandler.htm нет на диске)
<sytem.web>
...
</httpHandlers>
<add verb="*" path="TestHandler.htm" type="TestHandler"/>
</httpHandlers>
...
Я бы вот так написал:
{
if (file.ContentLength > 0)
{
file.SaveAs( Path.Combine(uploads.FullName, Path.GetFileName(file.FileName)) );
}
}
Оказалось, что foreach здесь не работает, т.к. энумератор оперирует именами файлов, а не их содержимым. Т.е. file будет типа string, а не HttpPostedFile.
А вот разындексирование возвращает HttpPostedFile.
Короче, странная эта колекция System.Web.HttpFileCollection.
Да, действительно странная (я в браузере тот ответ фигачил - в МСДН не смотрел), что-то вроде словаря, а интерфейс совсем не словарный; вон и перечисление толком не предоставляет.
Установить в IIS-е дефолтный документ для данного сайта (там по умолчанию Default.aspx).
[ATTACH]3485[/ATTACH]
[ATTACH]3485[/ATTACH]
Я ещё до IIS не дошел. :)
Пока пользуюсь встроенным в VS сервером.
Попробовал через web.config
<urlMappings enabled="true">
<add url="~/Default.aspx" mappedUrl="~/Status.ashx"/>
<add url="~/" mappedUrl="~/Status.ashx"/>
<add url="~" mappedUrl="~/Status.ashx"/>
</urlMappings>
..............
Первая строчка отрабатывает, т.е. при явном обращении к Default.aspx переадресуется на Status.ashx.
Остальные строки не работают :(