Cómo subir archivos al servidor en una aplicación ASP.NET MVC

Desarrollar una aplicación que nos permita subir archivos a un servidor desde un explorador Web es un proceso bastante sencillo, ya que la etiqueta <input type="file" /> hace prácticamente todo el trabajo por nosotros en lo que se refiere a la parte del "front-end".

Aún así, si queremos mantener un cierto control sobre estos archivos subidos al servidor, debemos plantearnos una estrategia de desarrollo en el "back-end" que nos permita tratar estas entidades físicas (archivos) como entidades lógicas que puedan ser fácilmente gestionables.

En este Post veremos cómo subir archivos al servidor en una aplicación ASP.NET MVC manteniendo a la vez un histórico simple en base de datos de los archivos subidos, con la posibilidad de descargarlos y eliminarlos del servidor.

Nota: Para realizar este ejemplo hemos utilizado Entity Framework como ORM (Object-Relational Mapper)Bootstrap 3.2 para dar formato y estilos a la Vista.

subir archivos servidor MVC

El modelo de datos

Como punto de inicio, debemos definir una clase (archivo.cs) que represente de manera lógica a los archivos que subiremos al servidor. Esta clase definirá las propiedades básicas de un archivo (nombre, extensión, path, descargas, etc.), así como los métodos necesarios para gestionarlos (subir, descargar, eliminar).

    [Table("archivos")]
    public class Archivo
    {
        // CONSTRUCTOR
        public Archivo()
        {
            this.Id = Guid.NewGuid();
            this.Creado = DateTime.Now;
        }

        // PROPIEDADES PÚBLICAS
        [Key]
        [Required]
        public Guid Id { get; set; }

        [Required]
        public DateTime Creado { get; set; }

        [Required]
        [StringLength(100)]
        public string Nombre { get; set; }

        [StringLength(4)]
        [Required]
        public string Extension { get; set; }

        [Required]
        public int Descargas { get; set; }              

        // PROPIEDADES PRIVADAS
        public string PathRelativo
        {
            get
            {
                return ConfigurationManager.AppSettings["PathArchivos"] +
                                            this.Id.ToString() + "." +
                                            this.Extension;
            }
        }

        public string PathCompleto
        {
            get
            {
                var _PathAplicacion = HttpContext.Current.Request.PhysicalApplicationPath;
                return Path.Combine(_PathAplicacion, this.PathRelativo);
            }
        }

        // MÉTODOS PÚBLICOS
        public void SubirArchivo(byte[] archivo)
        {
            File.WriteAllBytes(this.PathCompleto, archivo);
        }

        public byte[] DescargarArchivo()
        {
            return File.ReadAllBytes(this.PathCompleto);
        }

        public void EliminarArchivo()
        {
            File.Delete(this.PathCompleto);
        }
    }

Esta clase será la que utilizaremos como Modelo para crear nuestra tabla de archivos en la base de datos con Entity Framework. Supondremos que ya hemos definido el DbContext de la aplicación con el correspondiente DbSet Archivos, el código sería el siguiente:

    public class AppDbContext : DbContext 
    {
        public AppDbContext() : base ("DataModelConnectionString")
        {
        }
        public IDbSet<Archivo> Archivos { get; set; }
    }

Por último, definiremos la carpeta donde irán alojados los archivos que subamos al Servidor. En este caso crearemos la carpeta ArchivosApp en nuestro proyecto, y registraremos el Path en la sección <appSettings /> del Web.config.

<appSettings>
    <add key="PathArchivos" value="ArchivosApp/" />
    ...
</appSettings>

El Controlador

A continuación crearemos un nuevo Controlador llamado ArchivosController. En este, definiremos las Acciones necesarias para subir, descargar y eliminar nuestros archivos.

Básicamente, la gestión de los archivos a subir la realizaremos a través de un objeto de la clase HttpPostedFileBase. La Acción del Controlador encargada de subir los archivos al Servidor SubirArchivo(HttpPostedFileBase file), recibirá como parámetro un objeto del tipo HttpPostedFileBase que será capaz de "mapear" el archivo subido, pudiendo así acceder a su contenido, tamaño, nombre, path etc.

A la hora de descargar el archivo, recurriremos a la clase FileContentResult. Esta clase integrada en el Framework MVC, es capaz de recuperar el contenido de un archivo y enviarlo al navegador a través del ActionResult de la Acción encargada de descargar los archivos.

    public class ArchivosController : Controller
    {
        AppDbContext _dbContext;

        [HttpGet]
        public ActionResult Index()
        {
            List<Archivo> _archivos = new List<Archivo>();
            using (_dbContext = new AppDbContext())
            {
                // Recuperamos la Lista de los archivos subidos.
                _archivos = _dbContext.Archivos.OrderBy(x => x.Creado).ToList();
            }
            // Retornamos la Vista Index, con los archivos subidos.
            return View(_archivos);
        }

        [HttpPost]
        public ActionResult SubirArchivo(HttpPostedFileBase file)
        {
            if (file != null && file.ContentLength > 0)
            {
                // Extraemos el contenido en Bytes del archivo subido.
                var _contenido = new byte[file.ContentLength];
                file.InputStream.Read(_contenido, 0, file.ContentLength);

                // Separamos el Nombre del archivo de la Extensión.
                int indiceDelUltimoPunto = file.FileName.LastIndexOf('.');
                string _nombre = file.FileName.Substring(0, indiceDelUltimoPunto);
                string _extension = file.FileName.Substring(indiceDelUltimoPunto + 1,
                                    file.FileName.Length - indiceDelUltimoPunto - 1);

                // Instanciamos la clase Archivo y asignammos los valores.
                Archivo _archivo = new Archivo()
                {
                    Nombre = _nombre,
                    Extension = _extension,
                    Descargas = 0
                };

                try
                {
                    // Subimos el archivo al Servidor.
                    _archivo.SubirArchivo(_contenido);
                    // Guardamos en la base de datos la instancia del archivo
                    using (_dbContext = new AppDbContext())
                    {
                        _dbContext.Archivos.Add(_archivo);
                        _dbContext.SaveChanges();
                    }
                }
                catch (Exception ex)
                {
                    // Aquí el código para manejar la Excepción.
                }                
            }

            // Redirigimos a la Acción 'Index' para mostrar
            // Los archivos subidos al Servidor.
            return RedirectToAction("Index");
        }

        [HttpGet]
        public ActionResult DescargarArchivo(Guid id)
        {
            Archivo _archivo;
            FileContentResult _fileContent;

            using (_dbContext = new AppDbContext())
            {
                _archivo = _dbContext.Archivos.FirstOrDefault(x => x.Id == id);
            }

            if (_archivo == null)
            {
                return HttpNotFound();
            }
            else
            {
                try
                {
                    // Descargamos el archivo del Servidor.
                    _fileContent = new FileContentResult(_archivo.DescargarArchivo(), 
                                                         "application/octet-stream");
                    _fileContent.FileDownloadName = _archivo.Nombre + "." + _archivo.Extension;

                    // Actualizamos el nº de descargas en la base de datos.
                    using (_dbContext = new AppDbContext())
                    {
                        _archivo.Descargas++;
                        _dbContext.Archivos.Attach(_archivo);
                        _dbContext.Entry(_archivo).State = EntityState.Modified;
                        _dbContext.SaveChanges();
                    }

                    return _fileContent;
                }
                catch (Exception ex)
                {
                    return HttpNotFound();
                }
            }
        }

        [HttpGet]
        public ActionResult EliminarArchivo(Guid id)
        {
            Archivo _archivo;

            using (_dbContext = new AppDbContext())
            {
                _archivo = _dbContext.Archivos.FirstOrDefault(x => x.Id == id);
            }

            if (_archivo != null)
            {
                using (_dbContext = new AppDbContext())
                {
                    _archivo = _dbContext.Archivos.FirstOrDefault(x => x.Id == id);
                    _dbContext.Archivos.Remove(_archivo);
                    if (_dbContext.SaveChanges() > 0)
                    {
                        // Eliminamos el archivo del Servidor.
                        _archivo.EliminarArchivo();
                    }
                }
                // Redirigimos a la Acción 'Index' para mostrar
                // Los archivos subidos al Servidor.
                return RedirectToAction("Index");
            }
            else
            {
                return HttpNotFound();
            }                        
        }
    }

La Vista

Por último crearemos una Vista que nos permita subir los archivos al Servidor mediante un control <input type="file" /> y nos muestre una lista de los archivos subidos, con la posibilidad de descargarlos y eliminarlos. En este caso crearemos la Vista por defecto Index.cshtml que recibirá como modelo de datos un objeto del tipo List<Archivo>

Como podemos ver en el código, debemos definir un <form> de tipo POST con el atributo enctype = "multipart/form-data" tal y como indica la especificación sobre formularios de la W3C para subir archivos binarios a un servidor. En nuestro caso al estar trabajando con MVC, hemos utilizado el AjaxHelper @Html.BeginForm() para construir el tag <form />.

@model List<Archivo>

@{
    ViewBag.Title = "Index";
}

<h2>Upload Files</h2>
<br />

<div class="container">
    @using (Html.BeginForm("SubirArchivo", "Archivos", null,
                FormMethod.Post,
                new { enctype = "multipart/form-data", method = "post" }))
    {
        <div class="row">
            <div class="col-sm-6">
                <input type="file" name="file" />
            </div>
            <div class="col-md-6 left">
                <button type="submit" class="btn btn-sm btn-primary">
                    <span class="glyphicon glyphicon-open"></span>
                    &nbsp;Subir archivo
                </button>
            </div>
        </div>
    }
    <hr />
    <div class="row">
        <div class="col-md-12">
            @foreach (var item in Model)
            {
                <span>@item.Creado.ToShortDateString() - </span>
                <a href="@Url.Action("DescargarArchivo", "Archivos", new { id = item.Id })">
                    <span class="glyphicon glyphicon-save"></span>
                    <span>@item.Nombre.@item.Extension</span>
                </a>
                <span> - Descargas: @item.Descargas - </span>
                <a href="@Url.Action("EliminarArchivo", "Archivos", 
                         new { id = item.Id })" class="text-danger">
                    <span class="glyphicon glyphicon-remove text-danger small"></span>
                    Eliminar archivo
                </a>
                <br />
            }
        </div>
    </div>
</div>
   EtiquetasASP.NET .NET MVC C# ASP.NET Core

  Compartir


  Nuevo comentario

El campo Comentario es obligatorio.
El campo Nombre es obligatorio.

  Comentarios

Adam Adam

Gracias por el contenido muy detallado me ha servido mucho !! gracias y sigue subiendo contenidos para la ayuda de los demas!!
javier villalba javier villalba

Que bueno tu articulo gracias
Cristobal Cristobal

Puede mandar el codigo, para verlo correr. Porfavor
Andres Andres

Existe alguna forma de analizar el archivo antes de subirlo, esto para evitar que nos suban archivos con malware.
Rafael Acosta Administrador Rafael Acosta

@Robinson:

Gracias por tu comentario, para cualquier cuestión, no dudes en preguntar.


Robinson Robinson

Buenisimo
Rafael Acosta Administrador Rafael Acosta

@diseño web madrid:

Gracias por tu comentario. Compartir los artículos en redes sociales y foros de programación, es fundamental para que este Blog siga ofreciendo publicaciones de calidad y en español.


Un saludo.


dise&#241;o web  madrid diseño web madrid

No habia visitado tu blog por un tiempo, porque me pareció que era denso, pero los últimos posts son de buena calidad, así que supongo que voy a añadirte a mi lista de sitios web cotidiana. Te lo mereces amigo. :)

Saludos

[url=https://www.granota.eu/diseno-web-madrid.html]diseño web madrid[/url]


Utilizamos cookies propias y de terceros para mejorar nuestros servicios y ofrecerle una mejor experiencia de navegación. Si continúa navegando consideramos que acepta su uso. Más información   Acepto