寒假比赛总结(二) - 解决前后端分离模式下前端与后端文件交互的问题

前言

我所在的产品项目组的核心功能就是图片上传,但是同时在用户发布之前还能删除任意图片。之前我已经了解到input的File List对象是只可读,不可写的。这就让我十分尴尬,想了很多办法,包括克隆数组,删除图片就标记一下,甚至是通过将图片Base 64编码直接放进数组里显示(实践证明如果图片太大,其Base 64编码也会非常非常长,长到一种无法想象的地步,此时浏览器也将会行走在崩溃的边缘QAQ),等等,一周前又需要修改图片的方向,所以上传时仅仅依靠input的FileList对象时完全不靠谱的。

于是我又参考了其他大神的作品,发现其中很多人采用了Blob URL来展示图片。

关于Blob

引用MDN上对Blob构造函数的阐述:

Blob 对象表示不可变的类似文件对象的原始数据。Blob表示不一定是JavaScript原生形式的数据。File 接口基于Blob,继承了 blob的功能并将其扩展使其支持用户系统上的文件。

Blob类型通常用于处理大文件,同时提供一个URL,指向浏览器内存中的某个文件,并且具有和普通文件一样的接口。

如何与后端接口进行交互

于是我想到直接将Blob URL传给后端,但是后来发现这个URL只对浏览器当前标签页有效,所以直接将这个URL给后端时绝对不可行的。这个方法展示图片虽然能很简单地执行修改和删除,但是如何传给后端却是个很大的问题。

经过几番查找资料,终于在StackOverflow上找到了一个能解燃眉之急的方案:

https://stackoverflow.com/questions/14952052/convert-blob-url-to-normal-url

这个问题被采纳的答案中有这么一句话:

It is possible convert a blob: URL into a data: URL, at least in Chrome. You can use an AJAX request to "fetch" the data from the blob: URL (even though it's really just pulling it out of your browser's memory, not making an HTTP request).

看完之后恍然大悟,原来这个URL可以被AJAX取出来,然后将拿到的response放在新的File对象中,然后append到一个FormData中,然后放到Vue组件中,POST给后端。

以下是代码片段:

utils.js:

getImgURL: (i, vue) => {
    var originalUrl = vue.imgList[i]
    console.log(`Original URL is ${originalUrl}`)
    var xhr = new XMLHttpRequest
    var blobAsDataUrl
    xhr.responseType = 'blob'
    xhr.onload = () => {
      var recoveredBlob = xhr.response
      var reader = new FileReader()
      reader.onload = () => {
        blobAsDataUrl = reader.result
        vue.$store.commit('processImg', blobAsDataUrl)
        vue.uploadList.push(vue.$store.state.processed)
        vue.$store.commit('delProcessed')
      }
      reader.readAsDataURL(recoveredBlob)
    }
    xhr.open('GET', originalUrl)
    xhr.send()
  }

后端接收代码如下:

from flask import Flask, request, make_response, render_template, redirect, url_for
from werkzeug.utils import secure_filename 
from os import path
import random
import time
import hashlib

app = Flask(__name__)

def get_random():
    str1 = random.randint(0, 999999)
    str2 = time.time()
    str3 = "key"

    string = str(str1) + str(str2) + str3
    raw = hashlib.md5()
    raw.update(string.encode("utf8"))
    result = raw.hexdigest()[8: -8]
    return result

@app.route("/upload", methods = ['POST'])
def upload():
    if request.method == 'POST':
        f = request.files["image"]
        # print(request.form)
        print(request.files["image"])
        base_path = path.abspath(path.dirname(__file__))
        file_name = get_random() + '.png'
        file_full_name = base_path + '/' + file_name
        f.save(file_full_name)
        print(file_full_name)
        return file_name

@app.route("/test", methods = ['POST'])
def test():
    return "23333"

if __name__ == '__main__':
    app.run()

将最新的文章发送到你的邮箱

展示评论