Carga de archivos en ASP.net sin usar el control del servidor FileUpload

99

¿Cómo puedo obtener un formulario web ASP.net (v3.5) para publicar un archivo usando un viejo simple <input type="file" />?

No estoy interesado en usar el control de servidor ASP.net FileUpload.

Ronnie Overby
fuente

Respuestas:

133

En tu aspx:

<form id="form1" runat="server" enctype="multipart/form-data">
 <input type="file" id="myFile" name="myFile" />
 <asp:Button runat="server" ID="btnUpload" OnClick="btnUploadClick" Text="Upload" />
</form>

En código detrás:

protected void btnUploadClick(object sender, EventArgs e)
{
    HttpPostedFile file = Request.Files["myFile"];

    //check file was submitted
    if (file != null && file.ContentLength > 0)
    {
        string fname = Path.GetFileName(file.FileName);
        file.SaveAs(Server.MapPath(Path.Combine("~/App_Data/", fname)));
    }
}
Mathieu
fuente
3
@mathieu, lástima que su variante use runat="server", no es independiente del servidor de ASP.NET
Secreto
¿Qué sucede si hay más de 1 entradas y desea que se realicen funciones independientes con ambos conjuntos de archivos?
Neville Nazerane
mientras lo usa para cargar varios archivos, devuelve el mismo nombre de archivo para todos los archivos y, por lo tanto, guarda el primer archivo n número de veces. ¿Alguna idea de cómo superarlo?
sohaiby
@Martina Compruebe este fragmento de código para guardar varios archivos
sohaiby
1
@DanielJ. No hay forma posible de no usar el código subyacente si luego va a hacer algo con el archivo, es decir, almacenarlo en una base de datos. Claro, podría usar JavaScript directo para capturar la entrada y el archivo: var fileUploaded = document.getElementById("myFile").files[0];e incluso puede obtener los bytes usando readAsArrayBuffer: stackoverflow.com/questions/37134433/… - pero ¿qué va a hacer con todos esos bytes del lado del cliente? El OP quiere evitar por completo el control ASP .NET, no la comunicación del lado del servidor. -1 para ti.
vapcguy
40

Aquí hay una solución sin depender de ningún control del lado del servidor, tal como OP ha descrito en la pregunta.

Código HTML del lado del cliente:

<form action="upload.aspx" method="post" enctype="multipart/form-data">
    <input type="file" name="UploadedFile" />
</form>

Método Page_Load de upload.aspx:

if(Request.Files["UploadedFile"] != null)
{
    HttpPostedFile MyFile = Request.Files["UploadedFile"];
    //Setting location to upload files
    string TargetLocation = Server.MapPath("~/Files/");
    try
    {
        if (MyFile.ContentLength > 0)
        {
            //Determining file name. You can format it as you wish.
            string FileName = MyFile.FileName;
            //Determining file size.
            int FileSize = MyFile.ContentLength;
            //Creating a byte array corresponding to file size.
            byte[] FileByteArray = new byte[FileSize];
            //Posted file is being pushed into byte array.
            MyFile.InputStream.Read(FileByteArray, 0, FileSize);
            //Uploading properly formatted file to server.
            MyFile.SaveAs(TargetLocation + FileName);
        }
    }
    catch(Exception BlueScreen)
    {
        //Handle errors
    }
}
Aycan Yaşıt
fuente
Tenga cuidado con la llamada HTTPWebRequest que envía la ruta completa del archivo en MyFile.FileName. La llamada de WebClient solo envía el nombre del archivo. HTTPWebRequest hará que MyFile.SaveAs falle. Solo desperdiciaron horas persiguiendo a un cliente trabajando y un cliente no.
Bob Clegg
Solo usé en Request.Files[0]lugar de Request.Files["UploadedFile"]y mientras el OP estaba usando ASP .NET directo y no MVC, si alguien quiere usar esto para MVC, solo necesita en HttpPostedFileBaselugar de HttpPostedFile.
vapcguy
23

Tendrá que establecer el enctypeatributo de formto multipart/form-data; luego puede acceder al archivo cargado usando la HttpRequest.Filescolección.

csgero
fuente
Mi formulario de servidor estaba en una página maestra y no pude agregar datos de formulario / multiparte (ya que entonces estaría en cada página). Una solución rápida y sucia fue agregar un control FileUpload oculto a la página. WebForms luego agregó los datos multiparte / form-data automáticamente. Tuve que hacer esto porque estaba usando una entrada de archivo con Angular2 y no podía usar un control de servidor en una plantilla de componente.
Jason
9

usar el control HTML con un atributo de servidor runat

 <input id="FileInput" runat="server" type="file" />

Luego, en asp.net Codebehind

 FileInput.PostedFile.SaveAs("DestinationPath");

También hay algunas opciones de terceros que mostrarán el progreso si está interesado

cgreeno
fuente
3
Nuevamente, eso lo convierte en un control de servidor. ;)
ahwm
@ahwm El OP quería evitar el control ASP .NET FileUpload ( <asp:FileUpload ID="fileUpload1" runat="server"></asp:FileUpload>) Eso es diferente a decir que no se ponga un runat=serveren un inputcampo.
vapcguy
1
Entiendo que eso significa que no quieren usar controles ASP.NET. Que incluye el HtmlInputFilecontrol.
ahwm
8

Sí, puede lograr esto mediante el método de publicación ajax. en el lado del servidor puede usar httphandler. Por lo tanto, no estamos utilizando ningún control de servidor según sus requisitos.

con ajax también puede mostrar el progreso de la carga.

tendrá que leer el archivo como inputstream.

using (FileStream fs = File.Create("D:\\_Workarea\\" + fileName))
    {
        Byte[] buffer = new Byte[32 * 1024];
        int read = context.Request.GetBufferlessInputStream().Read(buffer, 0, buffer.Length);
        while (read > 0)
        {
            fs.Write(buffer, 0, read);
            read = context.Request.GetBufferlessInputStream().Read(buffer, 0, buffer.Length);
        }
    } 

Código de muestra

function sendFile(file) {              
        debugger;
        $.ajax({
            url: 'handler/FileUploader.ashx?FileName=' + file.name, //server script to process data
            type: 'POST',
            xhr: function () {
                myXhr = $.ajaxSettings.xhr();
                if (myXhr.upload) {
                    myXhr.upload.addEventListener('progress', progressHandlingFunction, false);
                }
                return myXhr;
            },
            success: function (result) {                    
                //On success if you want to perform some tasks.
            },
            data: file,
            cache: false,
            contentType: false,
            processData: false
        });
        function progressHandlingFunction(e) {
            if (e.lengthComputable) {
                var s = parseInt((e.loaded / e.total) * 100);
                $("#progress" + currFile).text(s + "%");
                $("#progbarWidth" + currFile).width(s + "%");
                if (s == 100) {
                    triggerNextFileUpload();
                }
            }
        }
    }
Samuel Joy
fuente
2
Tal vez debería agregar la sugerencia para agregar y fs.close()todo lo demás podría terminar en archivos ilegibles, ya que permanecen abiertos de alguna manera dentro de IIS.
wrongpink
@mistapink ¿El flujo de archivos no se cierra cuando la ejecución sale del bloque de uso?
Sebi
@Sebi parece que hace casi 3 años no lo hizo. No lo probé durante mucho tiempo, por lo que no puedo dar una respuesta definitiva aquí. Lo siento.
wrongpink
4

La colección Request.Files contiene todos los archivos cargados con su formulario, independientemente de si provienen de un control FileUpload o de un archivo <input type="file">.

Por lo tanto, puede escribir una etiqueta de entrada de archivo simple y antigua en el medio de su WebForm y luego leer el archivo cargado desde la colección Request.Files.

David
fuente
4

Como otros han respondido, Request.Files es una HttpFileCollection que contiene todos los archivos que se publicaron, solo necesita pedirle a ese objeto el archivo de esta manera:

Request.Files["myFile"]

Pero, ¿qué sucede cuando hay más de un marcado de entrada con el mismo nombre de atributo?

Select file 1 <input type="file" name="myFiles" />
Select file 2 <input type="file" name="myFiles" />

En el lado del servidor, el código anterior Request.Files ["myFile"] solo devuelve un objeto HttpPostedFile en lugar de los dos archivos. He visto en .net 4.5 un método de extensión llamado GetMultiple pero para versiones anteriores no existe, para el caso propongo el método de extensión como:

public static IEnumerable<HttpPostedFile> GetMultiple(this HttpFileCollection pCollection, string pName)
{
        for (int i = 0; i < pCollection.Count; i++)
        {
            if (pCollection.GetKey(i).Equals(pName))
            {
                yield return pCollection.Get(i);
            }
        }
}

Este método de extensión devolverá todos los objetos HttpPostedFile que tienen el nombre "myFiles" en HttpFileCollection si existe alguno.

Joey O
fuente
2

Control HtmlInputFile

He usado esto todo el tiempo.

Acechador de hecho
fuente
2
Eso requiere agregar, lo runat="server"que esencialmente lo convierte en un control de servidor, que no querían usar.
ahwm
@ahwm No, el OP no quiso usar el control específico de ASP .NET FileUpload. Eso es diferente a decir que no querían usar un control del lado del servidor, en general.
vapcguy
0
//create a folder in server (~/Uploads)
 //to upload
 File.Copy(@"D:\CORREO.txt", Server.MapPath("~/Uploads/CORREO.txt"));

 //to download
             Response.ContentType = ContentType;
             Response.AppendHeader("Content-Disposition", "attachment;filename=" + Path.GetFileName("~/Uploads/CORREO.txt"));
             Response.WriteFile("~/Uploads/CORREO.txt");
             Response.End();
Jean Pierre Yenque
fuente