asp.net - from - Cómo subir archivos enormes al blob de Azure desde una página web
download file from azure blob storage c# (4)
Internamente, ASP.NET tiene un espacio de direcciones de 2 GB, pero en realidad solo tiene menos de 1 GB de carga (consulte http://support.microsoft.com/?id=295626 ). Además, IIS 7 tiene un límite de 30 MB (consulte http://www.iislogs.com/steveschofield/iis7-post-40-adjusting-file-upload-size-in-iis7 ) y usted supuestamente debe ejecutar
appcmd set config "My Site/MyApp" -section:requestFiltering -requestLimits.maxAllowedContentLength:104857600 -commitpath:apphost
en el servidor para ir más allá de este límite de 30 MB. Pero, ¿cómo puedo ejecutar esto en mis servidores de Azure?
Además, de acuerdo con http://support.microsoft.com/?id=295626
Durante el proceso de carga, ASP.NET carga todo el archivo en la memoria antes de que el usuario pueda guardar el archivo en el disco.
, por lo que agotaré rápidamente el límite de memoria si muchos usuarios cargan archivos grandes a la vez. En mi código a continuación utilizo las transmisiones, pero supongo que todo el archivo se carga primero en la memoria. ¿Es este el caso?
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();
}
}
}
}
Conozco las herramientas de carga de páginas que no son web como http://azureblobuploader.codeplex.com/ , pero realmente necesito que se carguen desde una página web.
Entonces, mis preguntas son:
- Cómo subir archivos al blob de más de 2 GB desde una página web
- ¿Cómo puedo subir archivos desde una página web como una transmisión que no consume toda la memoria?
- Si la solución es escribir mi propio HttpModule o HttpHandler para manejar mi carga, ¿cómo puedo instalar esto en mis servidores Azure? ¿Puedo usar HttpHandlers como http://neatupload.codeplex.com/ en Azure?
- Este proyecto no está en SharePoint, pero sé que en SharePoint tienes algo llamado Proveedor de Blob y que puedes escribir el tuyo, ¿hay proveedores Blob para ASP.NET?
También puedo mencionar que mi código anterior funciona bien de forma predeterminada con archivos de menos de 30 MB. Uso SWFUpload V2.2.0 en el cliente.
Actualización 19. 19 de junio: @YvesGoeleven en Twitter me dio una pista sobre el uso de la Firma de Acceso Compartido (vea msdn.microsoft.com/en-us/library/ee395415.aspx) y cargó el archivo directamente en Azure Blob Storage sin pasando por el ASP.NET en absoluto. Creé un JSON WCF que devuelve un ut ut SAS válido para mi almacenamiento 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);
}
}
}
Funciona, pero no puedo usarlo con SWFUpload ya que usa el verbo HTTP POST y no el verbo HTTP PUT que Azure Blob Storage espera al crear un nuevo elemento blob. ¿Alguien sabe cómo evitar esto sin tener mi propio componente cliente de Silverlight o Flash personalizado que utiliza el verbo HTTP PUT? Quería una barra de progreso al cargar los archivos, por lo tanto, un formulario enviado que utiliza PUT no es óptimo.
Para aquellos interesados en el código de cliente (que no funcionará, ya que SWFUpload usa HTTP POST y no PUT, ya que Azure Blob Storage espera):
<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>
Actualización 19. 21:07 de junio:
Pensé que, dado que SWFUpload es de código abierto, descargué la fuente y cambié el verbo de POST a PUT, lamentablemente Flash Player URLRequestMethod no admite otros verbos que GET y POST. Encontré una supuesta solución
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"));
, pero esto solo funciona en Adobe Air y no con Flash Player.
He leído que SilverLight 3 y versiones posteriores admiten el verbo HTTP PUT, por lo que creo que tengo que escribir algunos códigos SilverLight para abrirme camino aquí. Encontré esta serie de artículos de blog que probablemente me ayuden aquí http://blog.smarx.com/posts/uploading-windows-azure-blobs-from-silverlight-part-1-shared-access-signatures .
Actualización @ 27. Junio ''11:
Ahora he logrado cargar archivos grandes (probados con archivos de 4,5 Gb) desde una página web usando un cliente Silverlight personalizado que escribí basado en el proyecto en http://blog.smarx.com/posts/uploading-windows- azur-blobs-from-silverlight-part-1-shared-access-firmas . Como Silverlight admite tanto el verbo HTTP PUT que requiere Azure Blob Storage como las cargas progresivas, ahora tengo la posibilidad de cargar archivos masivos directamente en Azure Blob Storage y no tengo que pasar por una solución ASP.NET, también obtener algunas barras de progreso agradables y el usuario puede cancelar en el medio de la carga si lo desea. El uso de la memoria en el servidor es mínimo, ya que no se carga todo el archivo antes de colocarlo en Azure Blob Storage. Uso una firma de acceso compartido (consulte msdn.microsoft.com/en-us/library/ee395415.aspx) que se proporciona desde un servicio WCF RESTfull a pedido. Creo que esta solución es la mejor que encontramos. Gracias.
Actualización @ 18. Julio ''11:
He creado un proyecto de código abierto con lo que encontré aquí:
De hecho, hice exactamente lo mismo recientemente. Creé una aplicación de Silverlight Client para manejar cortar los datos y enviarlos a Azure.
Este es un ejemplo de trabajo que seguí y que hace exactamente eso. Basta seguir esto y tu trabajo está casi hecho para ti.
Para esta parte de la pregunta:
appcmd set config "My Site/MyApp" -section:requestFiltering -requestLimits.maxAllowedContentLength:104857600 -commitpath:apphost
en el servidor para ir más allá de este límite de 30 MB. Pero, ¿cómo puedo ejecutar esto en mis servidores de Azure?
Puede hacer esto usando tareas de inicio - vea http://richardprodger.wordpress.com/2011/03/22/azure-iis7-configuration-with-appcmd/
Podemos cargar archivos muy grandes en almacenamiento azul mediante carga paralela. Eso significa que tenemos que dividir los archivos grandes en paquetes de archivos pequeños y descargarlos. Una vez completada la carga, podemos unir los paquetes al original. Para obtener el código completo, consulte el siguiente enlace http://tuvian.wordpress.com/2011/06/28/how-to-upload-large-size-fileblob-to-azure-storage-using-asp-netc/
no importa qué patrón de código utilices. Si escribe un código del lado del servidor, el archivo irá a su webrole y luego se producirán varias molestias, como el papeleo de reciclaje y la reintentación de cargas fallidas. Eliminé estos problemas a través de un control Silverlight del lado del cliente, que no solo cargaba los archivos con tolerancia a errores sino que también lo hacía a gran velocidad. Puede descargar mi muestra y leer cómo la construí desde: Elija su control de carga de archivos Azure: Silverlight y TPL o HTML5 y AJAX