Внутренне ASP.NET имеет адресное пространство 2 ГБ, но на самом деле у вас есть менее 1 ГБ свободного места для загрузки (см. http://support.microsoft.com/?id=295626). Кроме того, IIS 7 имеет ограничение в 30 МБ (см. http://www.iislogs.com/steveschofield/iis7-post-40-adjusting-file-upload-size-in-iis7), и вам, предположительно, придется запустить
appcmd set config "My Site/MyApp" -section:requestFiltering -requestLimits.maxAllowedContentLength:104857600 -commitpath:apphost
на сервере, чтобы выйти за пределы 30 МБ. Но как я могу запустить это на своих серверах Azure?
Кроме того, согласно http://support.microsoft.com/?id=295626
В процессе загрузки ASP.NET загружает весь файл в память, прежде чем пользователь сможет сохранить файл на диск.
, поэтому я быстро исчерпаю лимит памяти, если многие пользователи загружают большие файлы одновременно. В моем коде ниже я использую потоки, но я предполагаю, что в любом случае весь файл сначала загружается в память. Так ли это?
using System;
using System.Web.Security;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;
namespace WebPages
{
public partial class Upload : System.Web.UI.Page
{
CloudBlobClient BlobClient = null;
CloudBlobContainer BlobContainer = null;
void InitBlob()
{
// Setup the connection to Windows Azure Storage
var storageAccount = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
BlobClient = storageAccount.CreateCloudBlobClient();
// Get and create the container
BlobContainer = BlobClient.GetContainerReference("publicfiles");
}
protected void Page_Load(object sender, EventArgs e)
{
//if (Membership.GetUser() == null) return; // Only allow registered users to upload files
InitBlob();
try
{
var file = Request.Files["Filedata"];
var storageAccount = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
BlobClient = storageAccount.CreateCloudBlobClient();
// Make a unique blob name
var extension = System.IO.Path.GetExtension(file.FileName);
// Create the Blob and upload the file
var blobAddressUri = String.Format("{0}{1}", Guid.NewGuid(), extension);
var blob = BlobContainer.GetBlobReference(blobAddressUri);
blob.UploadFromStream(file.InputStream);
// Set the metadata into the blob
blob.Metadata["FileName"] = file.FileName;
//blob.Metadata["Submitter"] = Membership.GetUser().UserName;
blob.Metadata["Type"] = "Video";
blob.Metadata["Description"] = "Test";
blob.SetMetadata();
// Set the properties
blob.Properties.ContentType = file.ContentType;
blob.SetProperties();
}
catch(Exception ex)
{
System.Diagnostics.Trace.TraceError("Upload file exception: {0}", ex.ToString());
// If any kind of error occurs return a 500 Internal Server error
Response.StatusCode = 500;
Response.Write("An error occured while uploading the file");
Response.End();
}
}
}
}
Мне известны инструменты загрузки не веб-страниц, такие как http://azureblobuploader.codeplex.com/, но мне действительно нужно, чтобы они были загружены с веб-страницы.
Итак, мои вопросы:
- Как загрузить файлы в большой двоичный объект размером более 2 ГБ с веб-страницы
- Как мне загружать большие файлы с веб-страницы в виде потока, который не съедает всю память
- Если решение состоит в том, чтобы написать собственный HttpModule или HttpHandler для обработки моей загрузки, как я могу установить это на моих серверах Azure? Могу ли я использовать HttpHandlers, такие как http://neatupload.codeplex.com/ в Azure?
- Этого проекта нет в SharePoint, но я знаю, что в SharePoint у вас есть нечто, называемое поставщиком больших двоичных объектов, и что вы можете написать свое собственное. Существуют ли поставщики больших двоичных объектов для ASP.NET?
Я также могу упомянуть, что мой приведенный выше код по умолчанию отлично работает с файлами размером менее 30 МБ, я использую SWFUpload V2.2.0 на клиенте.
Обновление 19 июня, 19:09: @YvesGoeleven в Twitter дал мне совет по использованию подписи общего доступа (см. msdn.microsoft.com/en-us/library/ee395415.aspx) и загрузке файла напрямую в хранилище BLOB-объектов Azure, вообще не используя ASP.NET. Я создал JSON WCF, который возвращает действительный SAS ut в мое хранилище BLOB-объектов.
using System.ServiceModel;
using System.ServiceModel.Web;
namespace WebPages.Interfaces
{
[ServiceContract]
public interface IUpload
{
[OperationContract]
[WebInvoke(Method = "GET",
ResponseFormat = WebMessageFormat.Json)]
string GetUploadUrl();
}
}
--------
using System;
using System.IO;
using System.Runtime.Serialization.Json;
using System.ServiceModel.Activation;
using System.Text;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.StorageClient;
namespace WebPages.Interfaces
{
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class UploadService : IUpload
{
CloudBlobClient BlobClient;
CloudBlobContainer BlobContainer;
public UploadService()
{
// Setup the connection to Windows Azure Storage
var storageAccount = CloudStorageAccount.FromConfigurationSetting("DataConnectionString");
BlobClient = storageAccount.CreateCloudBlobClient();
// Get and create the container
BlobContainer = BlobClient.GetContainerReference("publicfiles");
}
string JsonSerialize(string url)
{
var serializer = new DataContractJsonSerializer(url.GetType());
var memoryStream = new MemoryStream();
serializer.WriteObject(memoryStream, url);
return Encoding.Default.GetString(memoryStream.ToArray());
}
public string GetUploadUrl()
{
var sasWithIdentifier = BlobContainer.GetSharedAccessSignature(new SharedAccessPolicy()
{
Permissions = SharedAccessPermissions.Write,
SharedAccessExpiryTime =
DateTime.UtcNow.AddMinutes(60)
});
return JsonSerialize(BlobContainer.Uri.AbsoluteUri + "/" + Guid.NewGuid() + sasWithIdentifier);
}
}
}
Он работает, но я не могу использовать его с SWFUpload, поскольку он использует команду HTTP POST, а не команду HTTP PUT, которую хранилище BLOB-объектов Azure ожидает при создании нового элемента BLOB-объектов. Кто-нибудь знает, как обойти это, не создавая собственный клиентский компонент Silverlight или Flash, который использует команду HTTP PUT? Мне нужен индикатор выполнения при загрузке файлов, поэтому отправленная форма, использующая PUT, не оптимальна.
Для тех, кто интересуется клиентским кодом (который не работает, поскольку SWFUpload использует HTTP POST, а не PUT, как ожидает хранилище BLOB-объектов Azure):
<div id="header">
<h1 id="logo"><a href="/">SWFUpload</a></h1>
<div id="version">v2.2.0</div>
</div>
<div id="content">
<h2>Application Demo (ASP.Net 2.0)</h2>
<div id="swfu_container" style="margin: 0px 10px;">
<div>
<span id="spanButtonPlaceholder"></span>
</div>
<div id="divFileProgressContainer" style="height: 75px;"></div>
<div id="thumbnails"></div>
</div>
</div>
<script type="text/javascript" language="javascript">
$(document).ready(function () {
$.ajax({
url: '/Interfaces/UploadService.svc/GetUploadUrl',
success: function (result) {
var parsedResult = $.parseJSON(result);
InitUploadFile(parsedResult);
}
});
function InitUploadFile(uploadUrl) {
//alert(uploadUrl);
var swfu = new SWFUpload({
// Backend Settings
upload_url: uploadUrl,
post_params: {
"ASPSESSID": "<%=Session.SessionID %>"
},
// File Upload Settings
file_size_limit: "100 MB",
file_types: "*.*",
file_types_description: "All file types",
file_upload_limit: "0", // Zero means unlimited
// Event Handler Settings - these functions as defined in Handlers.js
// The handlers are not part of SWFUpload but are part of my website and control how
// my website reacts to the SWFUpload events.
file_queue_error_handler: fileQueueError,
file_dialog_complete_handler: fileDialogComplete,
upload_progress_handler: uploadProgress,
upload_error_handler: uploadError,
upload_success_handler: uploadSuccess,
upload_complete_handler: uploadComplete,
// Button settings
button_image_url: "Images/swfupload/XPButtonNoText_160x22.png",
button_placeholder_id: "spanButtonPlaceholder",
button_width: 160,
button_height: 22,
button_text: '<span class="button">Select files <span class="buttonSmall">(2 MB Max)</span></span>',
button_text_style: '.button { font-family: Helvetica, Arial, sans-serif; font-size: 14pt; } .buttonSmall { font-size: 10pt; }',
button_text_top_padding: 1,
button_text_left_padding: 5,
// Flash Settings
flash_url: "Js/swfupload-2.2.0/swfupload.swf", // Relative to this file
custom_settings: {
upload_target: "divFileProgressContainer"
},
// Debug Settings
debug: false
});
}
});
</script>
Обновление 19 июня, 21:07:
Я решил, что поскольку SWFUpload является открытым исходным кодом, я загружаю исходный код и меняю команду с POST на PUT, но, к сожалению, Flash Player URLRequestMethod не поддерживает другие команды, кроме GET и POST. Я нашел предполагаемый обходной путь
private function BuildRequest():URLRequest {
// Create the request object
var request:URLRequest = new URLRequest();
request.method = URLRequestMethod.POST;
request.requestHeaders.push(new URLRequestHeader("X-HTTP-Method-Override", "PUT"));
, но это работает только в Adobe Air, а не во Flash Player.
Я читал, что SilverLight 3 и более поздние версии поддерживают команду HTTP PUT, поэтому я думаю, что мне нужно написать код SilverLight, чтобы добиться здесь своего. Я нашел эту серию статей в блоге, которая, вероятно, мне поможет здесь http://blog.smarx.com/posts/uploading-windows-azure-blobs-from-silverlight-part-1-shared-access-подписи.
Обновление от 27 июня 2011 г .:
Теперь мне успешно удалось загрузить большие файлы (проверено с файлами размером 4,5 ГБ) с веб-страницы с помощью специального клиента Silverlight, который я написал на основе проекта в http://blog.smarx.com/posts/uploading-windows-azure-blobs-from-silverlight-part-1-shared-access-signatures. Поскольку Silverlight поддерживает как команду HTTP PUT, которая требуется для хранилища BLOB-объектов Azure, так и поддержку прогрессивной загрузки, теперь у меня есть возможность загружать массивные файлы непосредственно в хранилище BLOB-объектов Azure, и мне не нужно использовать решение ASP.NET, я также получить несколько хороших индикаторов выполнения, и пользователь может отменить в середине загрузки, если он / она хочет. Использование памяти на сервере минимально, поскольку весь файл не выгружается до того, как он будет помещен в хранилище BLOB-объектов Azure. Я использую подпись общего доступа (см. Msdn.microsoft.com/en-us/library/ee395415.aspx), которая предоставляется службой WCF RESTfull по запросу. Я думаю, что это лучшее решение, которое мы нашли. Спасибо.
Обновление от 18 июля 2011 г .:
Я создал проект с открытым исходным кодом, используя то, что нашел здесь: