寒假比赛总结(二) - 解决前后端分离模式下前端与后端文件交互的问题
前言
我所在的产品项目组的核心功能就是图片上传,但是同时在用户发布之前还能删除任意图片。之前我已经了解到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()