Parcourir la source

Save zip downloaded folder

Jérôme BUISINE il y a 4 ans
Parent
commit
b439136dbd
5 fichiers modifiés avec 295 ajouts et 41 suppressions
  1. 19 7
      expe/templates/expe/expe_results.html
  2. 1 0
      expe/urls.py
  3. 48 33
      expe/views.py
  4. 166 0
      static/js/FileSaver.js
  5. 61 1
      static/js/results.js

+ 19 - 7
expe/templates/expe/expe_results.html

@@ -22,27 +22,34 @@
                 <ul class="list-group">
 
                 {% for name, folder in folders.items %} 
-                    <a href="#" class="date-folder-list">
+                    
                         <li class="list-group-item">
                             
                             <div class="row">
-                                <div class="col-md-11">{{name}} </div>
-                                <div class="col-md-1"><i class="fas fa-arrow-circle-right"></i></div>
+                                <div class="col-md-10">{{name}} </div>
+                                <div class="col-md-1 download-list" data-download-path="{{expe}}/{{name}}"><i class="fas fa-download"></i></div>
+                                <div class="col-md-1 date-folder-list"><i class="fas fa-arrow-circle-right"></i></div>
                             </div>
                             
-
                             <ul class="list-group files-list" style="display: none">
 
                                 <hr />
-                                {% for f in folder %}
-                                    <li class="list-group-item">{{f}}</li>
+                                {% for file in folder %}
+                                    <li class="list-group-item">
+                                    
+                                        <div class="row">
+                                            <div class="col-md-11">{{file}} </div>
+                                            <div class="col-md-1 download-list" data-download-path="{{expe}}/{{name}}/{{file}}"><i class="fas fa-download"></i></div>
+                                        </div>
+                                                
+                                    </li>
                                 {% endfor %}
                             </ul>
                         </li>
-                    </a>
                 {% endfor %}
 
                 </ul>
+            {% csrf_token %}
 
             {% else %}
                 <div class="alert alert-warning" role="alert">
@@ -59,6 +66,11 @@
     </div>
 
     {% block javascripts %}
+         <script type="text/javascript"> 
+            // Utils informations
+            var expe_name = "{{expe}}"
+        </script>
         <script src="{% static "js/results.js" %}"></script>
+        <script src="{% static "js/FileSaver.js" %}"></script>
     {% endblock %}
 {% endblock %}

+ 1 - 0
expe/urls.py

@@ -13,6 +13,7 @@ urlpatterns = [
     path('indications', views.indications, name='indications'),
     path('admin/results', views.list_results, name='results'),
     path('admin/results/<str:expe>', views.list_results, name='results_expe'),
+    path('admin/download', views.download_result, name='download')
 ]
 
 if settings.DEBUG is True:

+ 48 - 33
expe/views.py

@@ -15,7 +15,7 @@ from datetime import datetime
 import pickle 
 import time
 import zipfile
-from io import StringIO
+from io import BytesIO
 
 
 # expe imports
@@ -157,14 +157,9 @@ def list_results(request, expe=None):
             # init folder dictionnary
             folders = {}
 
-            print(folder_path)
-
             if os.path.exists(folder_path):
             
                 days = os.listdir(folder_path)
-                print(days)
-
-                folder = {}
 
                 for day in days:
                     day_path = os.path.join(folder_path, day)
@@ -178,43 +173,63 @@ def list_results(request, expe=None):
 
 
 @login_required(login_url="login/")
-def getfiles(request):
+def download_result(request):
     
-    day = request.POST.get('day')   
+    path = request.POST.get('path')
+    folder_path = os.path.join(settings.MEDIA_ROOT, cfg.output_expe_folder, path)
+
+    # Folder is required
+    if os.path.exists(folder_path):
+
+        # Open BytesIO to grab in-memory ZIP contents
+        s = BytesIO()
+
+        # check if file or folder
+        if os.path.isdir(folder_path):
+            
+            # get files from a specific day
+            filenames = os.listdir(folder_path)
 
-    # get files from a specific day
-    folder_path = os.path.join(settings.MEDIA_ROOT, cfg.model_expe_folder.format(day))
-    filenames = os.listdir(folder_path)
+            # Folder name in ZIP archive which contains the above files
+            # E.g [thearchive.zip]/somefiles/file2.txt
+            # FIXME: Set this to something better
+            zip_subdir = folder_path.split('/')[-1]
+            zip_filename = "%s.zip" % zip_subdir
 
-    # Folder name in ZIP archive which contains the above files
-    # E.g [thearchive.zip]/somefiles/file2.txt
-    # FIXME: Set this to something better
-    zip_subdir = "somefiles"
-    zip_filename = "%s.zip" % zip_subdir
+            # The zip compressor
+            zf = zipfile.ZipFile(s, "w")
 
-    # Open StringIO to grab in-memory ZIP contents
-    s = StringIO.StringIO()
+            for fpath in filenames:
+                
+                fpath = os.path.join(folder_path, fpath)
 
-    # The zip compressor
-    zf = zipfile.ZipFile(s, "w")
+                # Calculate path for file in zip
+                fdir, fname = os.path.split(fpath)
+                zip_path = os.path.join(zip_subdir, fname)
 
-    for fpath in filenames:
-        # Calculate path for file in zip
-        fdir, fname = os.path.split(fpath)
-        zip_path = os.path.join(zip_subdir, fname)
+                # Add file, at correct path
+                zf.write(fpath, zip_path)
 
-        # Add file, at correct path
-        zf.write(fpath, zip_path)
+            # Must close zip for all contents to be written
+            zf.close()
 
-    # Must close zip for all contents to be written
-    zf.close()
+            output_filename = zip_filename
 
-    # Grab ZIP file from in-memory, make response with correct MIME-type
-    resp = HttpResponse(s.getvalue(), mimetype = "application/x-zip-compressed")
-    # ..and correct content-disposition
-    resp['Content-Disposition'] = 'attachment; filename=%s' % zip_filename
+        else:
+            
+            # filename only
+            output_filename = folder_path
+
+        # Grab ZIP file from in-memory, make response with correct MIME-type
+        resp = HttpResponse(s.getvalue(), content_type="application/gzip")
+        # ..and correct content-disposition
+        resp['Content-Disposition'] = 'attachment; filename=%s' % zip_filename
+
+        return resp
+
+    else:
+        return Http404("Path does not exist")
 
-    return resp
 
 
 def refresh_data(request, expe_name, scene_name):

+ 166 - 0
static/js/FileSaver.js

@@ -0,0 +1,166 @@
+/*
+* FileSaver.js
+* A saveAs() FileSaver implementation.
+*
+* By Eli Grey, http://eligrey.com
+*
+* License : https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md (MIT)
+* source  : http://purl.eligrey.com/github/FileSaver.js
+*/
+
+// The one and only way of getting global scope in all environments
+// https://stackoverflow.com/q/3277182/1008999
+var _global = typeof window === 'object' && window.window === window
+  ? window : typeof self === 'object' && self.self === self
+  ? self : typeof global === 'object' && global.global === global
+  ? global
+  : this
+
+function bom (blob, opts) {
+  if (typeof opts === 'undefined') opts = { autoBom: false }
+  else if (typeof opts !== 'object') {
+    console.warn('Deprecated: Expected third argument to be a object')
+    opts = { autoBom: !opts }
+  }
+
+  // prepend BOM for UTF-8 XML and text/* types (including HTML)
+  // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
+  if (opts.autoBom && /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
+    return new Blob([String.fromCharCode(0xFEFF), blob], { type: blob.type })
+  }
+  return blob
+}
+
+function download (url, name, opts) {
+  var xhr = new XMLHttpRequest()
+  xhr.open('GET', url)
+  xhr.responseType = 'blob'
+  xhr.onload = function () {
+    saveAs(xhr.response, name, opts)
+  }
+  xhr.onerror = function () {
+    console.error('could not download file')
+  }
+  xhr.send()
+}
+
+function corsEnabled (url) {
+  var xhr = new XMLHttpRequest()
+  // use sync to avoid popup blocker
+  xhr.open('HEAD', url, false)
+  try {
+    xhr.send()
+  } catch (e) {}
+  return xhr.status >= 200 && xhr.status <= 299
+}
+
+// `a.click()` doesn't work for all browsers (#465)
+function click (node) {
+  try {
+    node.dispatchEvent(new MouseEvent('click'))
+  } catch (e) {
+    var evt = document.createEvent('MouseEvents')
+    evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80,
+                          20, false, false, false, false, 0, null)
+    node.dispatchEvent(evt)
+  }
+}
+
+var saveAs = _global.saveAs || (
+  // probably in some web worker
+  (typeof window !== 'object' || window !== _global)
+    ? function saveAs () { /* noop */ }
+
+  // Use download attribute first if possible (#193 Lumia mobile)
+  : 'download' in HTMLAnchorElement.prototype
+  ? function saveAs (blob, name, opts) {
+    var URL = _global.URL || _global.webkitURL
+    var a = document.createElement('a')
+    name = name || blob.name || 'download'
+
+    a.download = name
+    a.rel = 'noopener' // tabnabbing
+
+    // TODO: detect chrome extensions & packaged apps
+    // a.target = '_blank'
+
+    if (typeof blob === 'string') {
+      // Support regular links
+      a.href = blob
+      if (a.origin !== location.origin) {
+        corsEnabled(a.href)
+          ? download(blob, name, opts)
+          : click(a, a.target = '_blank')
+      } else {
+        click(a)
+      }
+    } else {
+      // Support blobs
+      a.href = URL.createObjectURL(blob)
+      setTimeout(function () { URL.revokeObjectURL(a.href) }, 4E4) // 40s
+      setTimeout(function () { click(a) }, 0)
+    }
+  }
+
+  // Use msSaveOrOpenBlob as a second approach
+  : 'msSaveOrOpenBlob' in navigator
+  ? function saveAs (blob, name, opts) {
+    name = name || blob.name || 'download'
+
+    if (typeof blob === 'string') {
+      if (corsEnabled(blob)) {
+        download(blob, name, opts)
+      } else {
+        var a = document.createElement('a')
+        a.href = blob
+        a.target = '_blank'
+        setTimeout(function () { click(a) })
+      }
+    } else {
+      navigator.msSaveOrOpenBlob(bom(blob, opts), name)
+    }
+  }
+
+  // Fallback to using FileReader and a popup
+  : function saveAs (blob, name, opts, popup) {
+    // Open a popup immediately do go around popup blocker
+    // Mostly only available on user interaction and the fileReader is async so...
+    popup = popup || open('', '_blank')
+    if (popup) {
+      popup.document.title =
+      popup.document.body.innerText = 'downloading...'
+    }
+
+    if (typeof blob === 'string') return download(blob, name, opts)
+
+    var force = blob.type === 'application/octet-stream'
+    var isSafari = /constructor/i.test(_global.HTMLElement) || _global.safari
+    var isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent)
+
+    if ((isChromeIOS || (force && isSafari)) && typeof FileReader !== 'undefined') {
+      // Safari doesn't allow downloading of blob URLs
+      var reader = new FileReader()
+      reader.onloadend = function () {
+        var url = reader.result
+        url = isChromeIOS ? url : url.replace(/^data:[^;]*;/, 'data:attachment/file;')
+        if (popup) popup.location.href = url
+        else location = url
+        popup = null // reverse-tabnabbing #460
+      }
+      reader.readAsDataURL(blob)
+    } else {
+      var URL = _global.URL || _global.webkitURL
+      var url = URL.createObjectURL(blob)
+      if (popup) popup.location = url
+      else location.href = url
+      popup = null // reverse-tabnabbing #460
+      setTimeout(function () { URL.revokeObjectURL(url) }, 4E4) // 40s
+    }
+  }
+)
+
+_global.saveAs = saveAs.saveAs = saveAs
+
+if (typeof module !== 'undefined') {
+  module.exports = saveAs;
+}

+ 61 - 1
static/js/results.js

@@ -6,17 +6,77 @@ function toggle(elem) {
       }
 }
 
+function toggleClass(elem, class1, class2) {
+    if (elem.className === class1) {
+        elem.className = class2;
+      } else {
+        elem.className = class1;
+      }
+}
+
+// use for call route to dowload content (as post request)
+function downloadContent(path){
+
+    const csrfToken = document.querySelectorAll('[name=csrfmiddlewaretoken]')[0].value
+
+    var xhttp = new XMLHttpRequest();
+    xhttp.open("POST", "/admin/download", true);
+    xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+    xhttp.setRequestHeader("X-CSRFToken", csrfToken);
+
+    xhttp.onreadystatechange = function (){
+        if (xhttp.readyState == 4 && xhttp.status == 200) {
+
+            // Try to find out the filename from the content disposition `filename` value
+            var disposition = xhttp.getResponseHeader('content-disposition');
+
+            // expe is find from django
+            var filename = expe_name + "_" + disposition.split('=')[1]
+      
+            var blob = new Blob([xhttp.response], {type: "octet/stream"});
+            saveAs(blob, filename);
+        }
+    };
+    xhttp.responseType = "arraybuffer";
+    xhttp.send("path=" + path); 
+}
+
 
 window.onload = function () {
 
+    // Display list of files from day folder
     elems = document.getElementsByClassName('date-folder-list')
     
     for (let item of elems) {
 
         item.onclick = function(event){
             event.preventDefault()
-            list = item.children[0].children[1]
+            currentElem = event.currentTarget
+
+            // get list element
+            list = currentElem.parentElement.nextElementSibling
+            
+            // display or hide list elements
             toggle(list)
+
+            // toggle arrow class for display effect
+            iconElem = currentElem.children[0]
+            toggleClass(iconElem, 'fas fa-arrow-circle-right', 'fas fa-arrow-circle-down')
+        }
+    }
+
+
+    elems = document.getElementsByClassName('download-list')
+
+    for (let downloadElem of elems) {
+
+        downloadElem.onclick = function(event){
+            event.preventDefault()
+
+            currentElem = event.currentTarget
+            pathDownload = currentElem.getAttribute('data-download-path')
+
+            downloadContent(pathDownload)
         }
     }
 }