简体   繁体   English

如何在flask网络服务器上检索数据作为文件对象?

[英]How to retrieve data as File object on flask webserver?

I am currently developing a web interface where you can draw a digit and upload it.我目前正在开发一个网络界面,您可以在其中绘制一个数字并上传它。 The image goes through a machine learning model of digit recognition and return the digit that has been transcribed by the user.图像经过数字识别的机器学习模型,并返回用户转录的数字。

HTML Code : HTML代码:

<canvas id="myCanvas">
        Sorry, your browser does not support HTML5 canvas technology.
</canvas>
<button onclick="clickonbutton()">Upload</button>

Canvas is used to draw digit and button to send it to a flask web server. Canvas 用于绘制数字和按钮以将其发送到 Flask Web 服务器。

Javascript Code : Javascript代码:

<script type="text/javascript">
function clickonbutton() {
        var canvas = document.getElementById('myCanvas');

        //Convert HTML5 Canvas to bytes-like object then File object
        canvas.toBlob(function(blob) {
                var file = new File([blob], "image.png", { type: "image/png", lastModified : new Date()});

                //Prepare a XMLHttpRequest to send the data with POST method to the server
                var request = new XMLHttpRequest();
                request.open("POST", "/");
                request.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
                //Sending file...
                request.send(file);
         });
}               
</script>

When the user click on the button "Upload" after editing the HTML5 canvas, it run this function which send the file to the server.当用户在编辑 HTML5 画布后单击“上传”按钮时,它会运行此功能将文件发送到服务器。

Python script (server-side) : Python 脚本(服务器端):

@app.route('/', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        #Convert MultiDict to Dict object then string
        data = str(request.form.to_dict())

        #Format string to remove Dict elements
        data = data.replace("{","")
        data = data.replace("}","")
        data = data.replace("'","")
        data = data.replace(": ","")

        #Convert string to bytes-like object
        img_bytes = data.encode()

        #Predict the digit of the user
        _, predicted_class, probabilities = get_prediction(image_bytes=img_bytes)
        predicted_class = int(predicted_class)
        classify(img=_, ps=probabilities)

        #Return the result of the digit prediction
        return render_template('result.html',class_name=predicted_class)
    return render_template('index.html')

On server-side, i get the data from request.form but these are in a ImmutableMultiDict object so i converted the data to string then bytes-like object.在服务器端,我从 request.form 获取数据,但这些数据位于 ImmutableMultiDict 对象中,因此我将数据转换为字符串,然后是字节类对象。 Unfortunately, conversion using the .replace() method corrupts the PNG file and there's no tool to do that conversion properly...不幸的是,使用 .replace() 方法进行转换会损坏 PNG 文件,并且没有工具可以正确地进行该转换...

Is there a solution to get the data directly in a File object without using an ImmutableMultiDict object ?是否有解决方案可以直接在 File 对象中获取数据而不使用 ImmutableMultiDict 对象?

So with help from this thread on using the Fetch API and this answer on HTML5 canvas drawing I figured this could be done with something like:因此,在此线程的帮助下, 使用 Fetch API以及有关 HTML5 画布绘图的此答案,我认为这可以通过以下方式完成:

<div id='sketch'>
  <canvas id="myCanvas">
  </canvas>
</div>

<button id='btn'>Upload</button>

<div id='result' style='font-size:2em;'></div>

Notice I've added a div to output the results (the class name) on the same page.请注意,我添加了一个div以在同一页面上输出结果(类名)。

Then in the Javascript use a FormData object: (I've missed out the drawing function for clarity):然后在 Javascript 中使用 FormData 对象:(为了清楚起见,我错过了绘图功能):


// Define the dom elements
const canvas = document.getElementById('myCanvas');
const btn = document.getElementById('btn');
const result = document.getElementById('result');

// This will handle the upload
const upload = (file) => {
  fetch('/', { // Your POST endpoint
    method: 'POST',
    body: file // This is your file object
  }).then(
    response => response.json() // if the response is a JSON object
  ).then(
    success => { result.innerText = success['class_name']; } // Write class_name into results div.
  ).catch(
    error => console.log(error) // Handle the error response object
  );
};

// Listener which handles the button click:
btn.addEventListener('click', function() {

  canvas.toBlob(function(blob) {
    var data = new FormData();
    data.append('file', blob);
    upload(data);
  });

}, false);

Now on the server side, this can be handled like a regular Flask file upload :现在在服务器端,这可以像普通的Flask 文件上传一样处理:

@app.route('/', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        file = request.files['file']

        #Convert string to bytes-like object
        img_bytes = file.read()

        #Predict the digit of the user
        _, predicted_class, probabilities = get_prediction(image_bytes=img_bytes)
        predicted_class = int(predicted_class)
        classify(img=_, ps=probabilities)

        #Return the result of the digit prediction
        return {'class_name': predicted_class}
    return render_template('index.html')

Any post request now returns a jsonified dictionary, which is in turn written to the results div on the page.现在任何发布请求都会返回一个 jsonified 字典,该字典依次写入页面上的结果div

There's probably room for improvment here, like some validation on the server-side aswell as rate limiting and error handling, but the basic concept works.这里可能有改进的空间,比如服务器端的一些验证以及速率限制和错误处理,但基本概念是有效的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM