Mercurial > libervia-web
diff libervia/web/pages/_browser/file_uploader.py @ 1532:106945841fbc
_browser (album): moved code to upload file to a separate `file_uploader` module:
this way, uploading logic can be re-used elsewhere.
author | Goffi <goffi@goffi.org> |
---|---|
date | Thu, 22 Jun 2023 16:36:01 +0200 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libervia/web/pages/_browser/file_uploader.py Thu Jun 22 16:36:01 2023 +0200 @@ -0,0 +1,110 @@ +import json +from bridge import Bridge +from browser import console as log, window +import dialog +from template import Template +bridge = Bridge() + +log.warning = log.warn + + +class FileUploader: + """Class handling HTTP upload and progress display""" + + def __init__(self, files_service, template_file, files_path=None, on_delete_cb=None): + self.files_service = files_service + self.files_path = files_path + self.item_tpl = Template(template_file) + self.on_delete_cb = on_delete_cb + self.uploaded = {} + + def on_progress(self, ev, item_elt): + if ev.lengthComputable: + percent = int(ev.loaded/ev.total*100) + self.update_progress(item_elt, percent) + + def on_load(self, file_, item_elt): + self.update_progress(item_elt, 100) + item_elt.classList.add("progress_finished") + item_elt.classList.remove("progress_started") + if self.on_delete_cb is not None: + delete_btn = item_elt.select_one('.action_delete') + if delete_btn is not None: + delete_btn.bind("click", self.on_delete_cb) + log.info(f"file {file_.name} uploaded correctly") + + def on_error(self, failure, file_, item_elt): + dialog.notification.show( + f"can't upload {file_.name}: {failure}", + level="error" + ) + + def update_progress(self, item_elt, new_value): + progress_elt = item_elt.select_one("progress") + if progress_elt is not None: + progress_elt.value = new_value + progress_elt.text = f"{new_value}%" + + def on_slot_cb(self, file_, upload_slot, item_elt): + put_url, get_url, headers = upload_slot + xhr = window.XMLHttpRequest.new() + xhr.open("PUT", put_url, True) + xhr.upload.bind('progress', lambda ev: self.on_progress(ev, item_elt)) + xhr.upload.bind('load', lambda ev: self.on_load(file_, item_elt)) + xhr.upload.bind('error', lambda ev: self.on_error(xhr.response, file_, item_elt)) + if self.files_path is not None: + xhr.setRequestHeader( + 'Xmpp-File-Path', + window.encodeURIComponent(self.files_path) + ) + xhr.setRequestHeader('Xmpp-File-No-Http', "true") + xhr.send(file_) + + # we update file data with the get URL + file_data = json.loads(item_elt.getAttribute("data-file")) + file_data["url"] = get_url + item_elt.setAttribute("data-file", json.dumps(file_data)) + + def on_slot_eb(self, file_, failure, item_elt): + dialog.notification.show( + f"Can't get upload slot: {failure['message']}", + level="error" + ) + item_elt.remove() + + def upload_files(self, files, container_elt): + """Start file upload, and add item_tpl to container_elt""" + log.info(f"uploading {len(files)} files") + for file_ in files: + url = window.URL.createObjectURL(file_) + + file_data = { + "name": file_.name, + "mime_type": file_.type, + } + template_file_data = file_data.copy() + # we don't want to open the file on click, it's not yet the + # uploaded URL + template_file_data["url"] = url + # we have no thumb yet, so we use the whole image + # TODO: reduce image for preview + template_file_data["thumb_url"] = url + + item_elt = self.item_tpl.get_elt({ + "file": template_file_data, + "uploading": True, + }) + item_elt.setAttribute("data-file", json.dumps(file_data)) + item_elt.classList.add("progress_started") + container_elt <= item_elt + + bridge.file_http_upload_get_slot( + file_.name, + file_.size, + file_.type or '', + self.files_service, + callback=lambda upload_slot, file_=file_, item_elt=item_elt: + self.on_slot_cb(file_, upload_slot, item_elt), + errback=lambda failure, file_=file_, item_elt=item_elt: + self.on_slot_eb(file_, failure, item_elt), + )