Subir múltiples archivos con progreso en web2py

Subir múltiples archivos con progreso en web2py



Descarga: web2py-fileuploader.zip.

Con ayuda de un poco de JavaScript, podemos hacer uso de las funcionalidades de carga de archivos que provee web2py (en particular, aquellas que competen a la seguridad de una aplicación web) para permitir al usuario subir múltiples ficheros arrastrándolos desde el ordenador y soltándolos en nuestra aplicación y viendo su respectivo progreso.

Subir archivos con progreso en web2py

Para ello estaremos usando el magnífico dmuploader, un pequeño plugin de jQuery que nos facilitará toda la implementación del control en el navegador web. Lo primero que debemos hacer es descargar sus archivos de distribución y el demo desde GitHub. En particular, debemos copiar en nuestra aplicación de web2py los siguientes archivos:

  1. demo/demo-config.js
  2. demo/demo-ui.js
  3. dist/js/jquery.dm-uploader.min.js
  4. dist/css/jquery.dm-uploader.min.css

Ubicándolos en las siguientes rutas respectivamente:

  1. static/js/demo-config.js
  2. static/js/demo-ui.js
  3. static/js/jquery.dm-uploader.min.js
  4. static/css/jquery.dm-uploader.min.css

Luego, creamos el HTML en nuestra vista (por ejemplo, views/controllers/index.html) con el siguiente código:

{{extend 'layout.html'}}

{{block head}}
    <link href='{{=URL("static", "css/jquery.dm-uploader.min.css")}}' rel="stylesheet">
{{end}}

{{block page_js}}
    <script src='{{=URL("static", "js/jquery.dm-uploader.min.js")}}'></script>
    <script src='{{=URL("static", "js/demo-ui.js")}}'></script>
    <script src='{{=URL("static", "js/demo-config.js")}}'></script>
    
    <!-- File item template -->
    <script type="text/html" id="files-template">
      <li class="media">
        <div class="media-body mb-1">
          <p class="mb-2">
            <strong>%%filename%%</strong> - Status: <span class="text-muted">Waiting</span>
          </p>
          <div class="progress mb-2">
            <div class="progress-bar progress-bar-striped progress-bar-animated bg-primary" 
              role="progressbar"
              style="width: 0%" 
              aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
            </div>
          </div>
          <hr class="mt-1 mb-1" />
        </div>
      </li>
    </script>

    <!-- Debug item template -->
    <script type="text/html" id="debug-template">
      <li class="list-group-item text-%%color%%"><strong>%%date%%</strong>: %%message%%</li>
    </script>
{{end}}

<div class="row">
  <div class="col-md-6 col-sm-12">
    
    <!-- Our markup, the important part here! -->
    <div id="drag-and-drop-zone" class="dm-uploader p-5">
      <h3 class="mb-5 mt-5 text-muted">Drag & drop files here</h3>

      <div class="btn btn-primary btn-block mb-5">
          <span>Open the file Browser</span>
          <input type="file" title='Click to add Files' />
      </div>
    </div><!-- /uploader -->

  </div>
  <div class="col-md-6 col-sm-12">
    <div class="card h-100">
      <div class="card-header">
        File List
      </div>

      <ul class="list-unstyled p-2 d-flex flex-column col" id="files">
        <li class="text-muted text-center empty">No files uploaded.</li>
      </ul>
    </div>
  </div>
</div><!-- /file list -->

Nótese que estamos usando los bloques de código head y page_js (definidos por views/layout.html) para incluir los archivos de CSS y JavaScript previamente copiados.

Ahora, consideremos una tabla de la base de datos con la siguiente estructura (por ejemplo, en models/db.py):

db.define_table(
    "upload",
    Field("upload_file", type="upload")
)

Esto nos permitirá hacer uso de las funcionalidades para carga de archivos de web2py.

A continuación definimos las funciones correspondientes en el controlador (en mi caso, controllers/default.py).

# -*- coding: utf-8 -*-


def get_download_url(upload_file):
    folder = "uploads"
    fullfilename = db.upload.upload_file.retrieve(
        upload_file, nameonly=True)[1]
    filename = fullfilename[fullfilename.find(folder) + len(folder) + 1:]
    filename = filename.replace("\\", "/")
    return URL("download", args=filename)


def index():
    return {}


def upload_file():
    """
    Esta función es invocada vía AJAX por el plugin dmuploader.
    """
    if request.env.request_method != "POST":
        raise HTTP(404)
    upload_file = db.upload.upload_file.store(
        request.post_vars.file,
        filename=request.post_vars.file.filename
    )
    db.upload.insert(upload_file=upload_file)
    return response.json({
        "status": "ok",
        "path": get_download_url(upload_file)
    })


@cache.action()
def download():
    """
    allows downloading of uploaded files
    http://..../[app]/default/download/[filename]
    """
    return response.download(request, db)

Y por último debemos alterar el archivo demo-config.js para que invoque la función upload_file() definida en el controlador. Para esto, cambiamos la línea

    url: 'backend/upload.php',

por

    url: '/fileuploader/default/upload_file',

Reemplazando fileupload por el nombre de tu aplicación de web2py o bien indicando la ruta correspondiente (en caso que hayas utilizado otro controlador o URL amigables).

¡Listo! Según el código que hemos utilizado aquí, los nombres de los archivos cargados serán guardados en la tabla upload y el contenido, en la carpeta uploads. Puedes descargar este ejemplo funcional desde el enlace al comienzo del artículo.



Deja un comentario