Use dropzone and pydrop for file uploading

This commit is contained in:
Daniel Markstedt 2021-09-21 22:51:06 -07:00
parent a9b7d43ef6
commit 4443f42d67
4 changed files with 65 additions and 36 deletions

View File

@ -11,3 +11,4 @@ waitress==1.4.4
zope.event==4.5.0 zope.event==4.5.0
zope.interface==5.1.2 zope.interface==5.1.2
protobuf==3.17.3 protobuf==3.17.3
pydrop==0.0.6

View File

@ -21,6 +21,11 @@
<meta name="theme-color" content="#ffffff"> <meta name="theme-color" content="#ffffff">
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.9.2/min/dropzone.min.css"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.9.2/min/basic.min.css"/>
<script type="application/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.9.2/min/dropzone.min.js">
</script>
<div class="content"> <div class="content">
<div class="header"> <div class="header">

View File

@ -172,30 +172,30 @@
<table style="border: none"> <table style="border: none">
<tr style="border: none"> <tr style="border: none">
<td style="border: none; vertical-align:top;"> <td style="border: none; vertical-align:top;">
<form id="uploadForm" action="/files/upload/" onchange="fileSelect(event)" method="post" enctype="multipart/form-data"> <form method="POST" action="/files/upload" class="dropzone dz-clickable" id="dropper" enctype="multipart/form-data">
<label for="file">File:</label>
<input type="file" name="file" accept="{{valid_file_suffix}}" />
<input type="submit" value="Upload" />
</form> </form>
</td> </td>
</tr> </tr>
</table> </table>
<p><small>Supported file types: {{valid_file_suffix}}</small></p> <script type="application/javascript">
<script> Dropzone.options.dropper = {
function fileSelect(e) { paramName: 'file',
document.getElementById("uploadForm").setAttribute('action', "/files/upload/" + e.target.files[0].name) chunking: true,
console.log(e.target.files[0].name); forceChunking: true,
url: '/files/upload',
maxFilesize: 2049, // megabytes
chunkSize: 1000000 // bytes
} }
</script> </script>
<p><small>Recognized file types: {{valid_file_suffix}}</small></p>
<hr/> <hr/>
<h2>Download File from Web</h2> <h2>Download File from Web</h2>
<p>Given a URL, download that file to the <tt>{{base_dir}}</tt></p> <p>Given a URL, download that file to the <tt>{{base_dir}}</tt></p>
<table style="border: none"> <table style="border: none">
<tr style="border: none"> <tr style="border: none">
<td style="border: none; vertical-align:top;"> <td style="border: none; vertical-align:top;">
<form action="/files/download_image" method="post"> <form action="/files/download_image" method="post">
<label for="url">URL:</label> <label for="url">URL:</label>
<input type="text" placeholder="URL" name="url" /> <input type="text" placeholder="URL" name="url" />

View File

@ -1,3 +1,4 @@
import logging
from flask import ( from flask import (
Flask, Flask,
render_template, render_template,
@ -7,6 +8,7 @@ from flask import (
redirect, redirect,
send_file, send_file,
send_from_directory, send_from_directory,
make_response,
) )
from file_cmds import ( from file_cmds import (
@ -46,6 +48,7 @@ from ractl_cmds import (
from settings import * from settings import *
app = Flask(__name__) app = Flask(__name__)
log = logging.getLogger('pydrop')
@app.route("/") @app.route("/")
@ -161,7 +164,7 @@ def drive_create():
size = request.form.get("size") size = request.form.get("size")
file_type = request.form.get("file_type") file_type = request.form.get("file_type")
file_name = request.form.get("file_name") file_name = request.form.get("file_name")
# Creating the image file # Creating the image file
process = create_new_image(file_name, file_type, size) process = create_new_image(file_name, file_type, size)
if process["status"] == True: if process["status"] == True:
@ -483,30 +486,50 @@ def download_img():
return redirect(url_for("index")) return redirect(url_for("index"))
@app.route("/files/upload/<filename>", methods=["POST"]) @app.route("/files/upload", methods=["POST"])
def upload_file(filename): def upload_file():
if not filename:
flash("No file provided.", "error")
return redirect(url_for("index"))
from os import path
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
file_path = path.join(app.config["UPLOAD_FOLDER"], secure_filename(filename)) from os import path
if path.isfile(file_path): import pydrop
flash(f"{filename} already exists.", "error")
return redirect(url_for("index"))
from io import DEFAULT_BUFFER_SIZE file = request.files['file']
binary_new_file = "bx"
with open(file_path, binary_new_file, buffering=DEFAULT_BUFFER_SIZE) as f: save_path = path.join(app.config["UPLOAD_FOLDER"], secure_filename(file.filename))
chunk_size = DEFAULT_BUFFER_SIZE current_chunk = int(request.form['dzchunkindex'])
while True:
chunk = request.stream.read(chunk_size) # If the file already exists it's ok if we are appending to it,
if len(chunk) == 0: # but not if it's new file that would overwrite the existing one
break if path.exists(save_path) and current_chunk == 0:
f.write(chunk) # 400 and 500s will tell dropzone that an error occurred and show an error
# TODO: display an informative success message return make_response(('File already exists', 400))
return redirect(url_for("index", filename=filename))
try:
with open(save_path, 'ab') as f:
f.seek(int(request.form['dzchunkbyteoffset']))
f.write(file.stream.read())
except OSError:
# log.exception will include the traceback so we can see what's wrong
log.exception('Could not write to file')
return make_response(("Not sure why,"
" but we couldn't write the file to disk", 500))
total_chunks = int(request.form['dztotalchunkcount'])
if current_chunk + 1 == total_chunks:
# This was the last chunk, the file should be complete and the size we expect
if path.getsize(save_path) != int(request.form['dztotalfilesize']):
log.error(f"File {file.filename} was completed, "
f"but has a size mismatch."
f"Was {path.getsize(save_path)} but we"
f" expected {request.form['dztotalfilesize']} ")
return make_response(('Size mismatch', 500))
else:
log.info(f'File {file.filename} has been uploaded successfully')
else:
log.debug(f'Chunk {current_chunk + 1} of {total_chunks} '
f'for file {file.filename} complete')
return make_response(("Chunk upload successful", 200))
@app.route("/files/create", methods=["POST"]) @app.route("/files/create", methods=["POST"])