DEV Community

Jain Wun
Jain Wun

Posted on

純JS實作照片上傳、下載與預覽

更多會員限定文章可以到patreon觀看


完整code可以到以下gist

Client端HTML

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> img { width: 150px; } </style> </head> <body> <input type="file" id="file-uploader" accept="image/png" multiple="multiple"/> <img id='preview'> </body> </html> 
Enter fullscreen mode Exit fullscreen mode
  • input tag
    • type設成file,讓使用者能選擇檔案
    • accept設定使用者能選的格式,如果要所有照片格式就改成image/*
    • multiple讓使用者能選擇多個檔案

Client端JS

我們會用JSON來傳遞資料(也可以用form-data)

取得使用者選擇的照片

讀進來的每個檔案會是blob(Binary Large Object)

const fileUploader = document.querySelector('#file-uploader'); fileUploader.addEventListener('change', (e) => { console.log(e.target.files); // get list of file objects }); 
Enter fullscreen mode Exit fullscreen mode

e.target.files是fileList

將照片轉成base64 string

function display_img(curFiles) { const curFile = curFiles[0]; const reader = new FileReader(); // 這會在readAS後才執行 reader.onload = function (e) { // console.log('file:', e.target.result); // base64 document.querySelector('#preview').src = e.target.result; }; // to data url reader.readAsDataURL(curFile); } 
Enter fullscreen mode Exit fullscreen mode

img.src能顯示url with base64

將blob轉成array buffer

相較於buffer, blob保留了照片的資訊,像是檔名、MIME type等

function blob2buffer(blob) { return new Promise((resolve, reject) => { var arrayBuffer; var fileReader = new FileReader(); fileReader.onload = function (event) { arrayBuffer = event.target.result; resolve(arrayBuffer); }; fileReader.readAsArrayBuffer(blob); return arrayBuffer; }); } 
Enter fullscreen mode Exit fullscreen mode

先將blob轉成buffer,方便後續建成array

將array轉成字串post到server

buffer不能直接操作,要先轉成指定type的array

arrayBuffer = await blob2buffer(pic); fetch(url, { method: 'POST', // flask need to set header of json headers: { 'content-type': 'application/json' }, body: JSON.stringify({ item_id: 1, format: 'png', img: Array.from(new Uint8Array(arrayBuffer)), }), }) 
Enter fullscreen mode Exit fullscreen mode

轉成array前要先將buffer轉成typed array

讀取server端傳回來的array

產生blob的url (不同於前面產base64的fileReader)

function display_arr_img(arr) { document.querySelector('#preview').src = URL.createObjectURL(new Blob([new Uint8Array(arr)], {type: "image/png"})); } 
Enter fullscreen mode Exit fullscreen mode

將array轉回typed array,再將其變回blob


Server端API

這裡我們用python + flask作為例子

DataBase

這裡我們用MySQL,然後照片的欄位type是blob,要注意blob有分可以存的size

  • TINYBLOB: maximum length of 255 bytes
  • BLOB: maximum length of 65,535 bytes => 64kb
  • MEDIUMBLOB: maximum length of 16,777,215 bytes
  • LONGBLOB: maximum length of 4,294,967,295 bytes

連接到資料庫

import pymysql connection = pymysql.connect(host='localhost', user='root', password='password', db='db01', charset='', cursorclass=pymysql.cursors.DictCursor) 
Enter fullscreen mode Exit fullscreen mode

注意pymysql不支援connection string

Python Code

所需套件在requirements.txt內

存到blob的好處是不會讓照片被額外index (會導致檔案大小增幅30%)

@app.route('/upload/json/img_raw', methods=['POST']) def raw_img(): json_data = flask.request.json # save to db  with connection.cursor() as cursor: sql = "UPDATE table1 SET `img_raw`=%s WHERE id=%s" try: # img現在是list, 可以用json string/ byterarray/ base64存進資料庫  cursor.execute(sql, (json.dumps(json_data['img']), json_data['item_id'])) connection.commit() except Exception as err: connection.rollback() return flask.jsonify({'err': err}) # get a picture  sql = "SELECT * FROM table1" cursor.execute(sql) result = cursor.fetchone() r = result[0]['img_raw'] r = json.loads(r.decode()) # utf-8, 也可用ascii  return flask.jsonify({'img': r}) 
Enter fullscreen mode Exit fullscreen mode

存進mysql blob欄位後,會變成binary object

  • json.dumps(json_data['img']
    • 我們要將List轉成json style的string才能存進資料庫
  • json.loads(r.decode())
    • 存資料庫拿出來的會是binary object,我們要先將其轉回字串
    • 然後再將字串 parse回list

Top comments (0)