Flask: Drag & Drop + Click & Select example | single page app
Flask: Drag & Drop + Click & Select | example single page app
I needed to create a simple web page that takes the MS Excel file as input, reads it, and shows the result.
I made my analysis for available solutions and I found out the common way is to use Dropzone.js or flask-dropzone.
It turns out that it is quite overkilling for my solution: it is focused on images, it allows multiple files uploads but the greatest problem for me was to create a single-page app (result is shown on the same page as upload).
After some searching and reading manuals I finished with the following result:
Project structure
MyProject │ app.py │ ├───static │ app.css │ app.js │ ├───templates │ index.html │ response.html │ └───uploads
Code
app.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from flask import Flask, jsonify, redirect, render_template, request, url_for
from werkzeug.utils import secure_filename
app = Flask(__name__)
app.config["UPLOAD_FOLDER"] = "uploads"
@app.route("/")
def index():
return render_template("index.html")
@app.route("/uploadfile", methods=["POST"])
def send_file():
fileob = request.files["file_for_upload"]
filename = secure_filename(fileob.filename)
save_path = "{}/{}".format(app.config["UPLOAD_FOLDER"], filename)
fileob.save(save_path)
message = f"successful_upload of {filename}"
return jsonify(
{
"htmlresponse": render_template("response.html", msg=message)
}
)
if __name__ == "__main__":
app.run(debug=True, port=5555)
static/app.css
.dropzone {
box-shadow: 0 0 8px rgb(207, 207, 207);
border: 1px solid #dedede;
border-radius: 15px;
background: #f5f5f5;
text-align: center;
font-family: Roboto;
cursor: pointer;
}
.dropzone:hover {
box-shadow: inset 1px 1px 1px #999;
}
.dropzone i {
font-size: 5rem;
}
.dropzone b {
font-size: 1.3rem;
}
.dropzone .dz-message {
color: rgba(0,0,0,.54);
font-weight: 500;
font-size: initial;
text-transform: uppercase;
}
static/app.js
$(function() {
function selectFile() {
var inputFile = document.createElement("input");
inputFile.type = "file";
// filter input files to MS Excel
inputFile.accept =
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
+ ",application/vnd.ms-excel";
inputFile.onchange = function() {
var files = Array.from(inputFile.files);
$("#targetLayer").hide(); // hide result area
uploadFiles(files);
};
inputFile.click();
};
var showUploadResult = function(data) {
$("#targetLayer").show();
// append to html: $("#targetLayer").append(data.htmlresponse);
$("#targetLayer").html(data.htmlresponse);
};
var showError = function(data) {
$("#targetLayer").show();
$("#targetLayer").html(data.statusText);
};
var dragHandler = function(evt) {
evt.preventDefault();
};
function uploadFiles(files) {
var formData = new FormData();
formData.append("file_for_upload", files[0]);
var req = {
url: "/uploadfile",
method: "post",
processData: false,
contentType: false,
data: formData
};
var promise = $.ajax(req);
promise.then(showUploadResult, showError);
};
var dropHandler = function(evt) {
evt.preventDefault();
var files = evt.originalEvent.dataTransfer.files;
uploadFiles(files);
};
var handlers = {
click: selectFile,
dragover: dragHandler,
drop: dropHandler,
};
$(".dropzone").on(handlers);
});
templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>Sample app</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,400i,500,500i,700" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<link href="{{url_for('static', filename='app.css')}}" rel="stylesheet" type="text/css" />
</head>
<body>
<div class="container">
<h1>Drag & Drop + Click & Select example</h1>
<div id="droparea" class="dropzone dz-message" style="width: 20%;">
<i class="material-icons text-muted">cloud_upload</i><br/>
<b>Drop your file<br/>here</b>
</div>
</div>
<div class="container" id="targetLayer" style="display:none;"></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="{{url_for('static', filename='app.js')}}"></script>
</body>
</html>
templates/response.html
<p><i style="color:gray">{{ msg }}</i></p>
Comments
Post a Comment