這次我要講述的是在React-Flask框架上開發上傳組件的技巧。我目前主要以React開發前端,在這個過程中認識到了許多有趣的前端UI框架——React-BootstrapAnt DesignMaterial UIBulma等。而比較流行的上傳組件也不少,而目前用戶比較多的是 jQuery-File-UploadDropzone,而成長速度快的新晋有Uppyfilepond。比較惋惜的是Fine-Uploader的作者自2018年後就决定不再維護了,原因作為後來者的我就不多過問了,但請各比特尊重每一比特開源作者的勞動成果。

這裏我選擇React-Dropzone,原因如下:

  1. 基於React開發,契合度高
  2. 網上推薦度高,連Material UI都用他開發上傳組件
  3. 主要以 DragDrop 為主,但是對於傳輸邏輯可以由開發者自行設計。例如嘗試用socket-io來傳輸file chunks。對於node全棧估計可行,但是我這裏使用的是Flask,需要將Blob轉ArrayBuffer。但是如何將其在Python中讀寫,我就沒進行下去了。

實例演示

1. axios上傳普通文件:

通過yarn將react-dropzone和引入:

yarn add react-dropzone axios

前端js如下(如有缺失,請自行修改):

import React, {
useState,
useCallback,
useEffect,
} from 'react';
import {useDropzone} from 'react-dropzone';
import "./dropzone.styles.css"
import InfiniteScroll from 'react-infinite-scroller';
import {
List,
message,
// Avatar,
Spin,
} from 'antd';
import axios from 'axios'; /**
* 計算文件大小
* @param {*} bytes
* @param {*} decimals
* @returns
*/
function formatBytes(bytes, decimals = 2) {
if (bytes === 0) return '0 Bytes'; const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
} /**
* Dropzone 上傳文件
* @param {*} props
* @returns
*/
function DropzoneUpload(props) {
const [files, setFiles] = useState([])
const [loading, setLoading] = useState(false);
const [hasMore, setHasMore] = useState(true); const onDrop = useCallback(acceptedFiles => {
setLoading(true);
const formData = new FormData();
smallFiles.forEach(file => {
formData.append("files", file);
});
axios({
method: 'POST',
url: '/api/files/multiplefiles',
data: formData,
headers: {
"Content-Type": "multipart/form-data",
}
})
then(resp => {
addFiles(acceptedFiles);
setLoading(false);
});
}, [files]); // Dropzone setting
const { getRootProps, getInputProps } = useDropzone({
multiple:true,
onDrop,
}); // 删除附件
const removeFile = file => {
const newFiles = [...files]
newFiles.splice(newFiles.indexOf(file), 1)
setFiles(newFiles)
} useEffect(() => {
// init uploader files
setFiles([])
},[]) return (
<section className="container">
<div {...getRootProps({className: 'dropzone'})}>
<input {...getInputProps()} />
<p>拖動文件或點擊選擇文件</p>
</div> <div className="demo-infinite-container">
<InfiniteScroll
initialLoad={false}
pageStart={0}
loadMore={handleInfiniteOnLoad}
hasMore={!loading && hasMore}
useWindow= {false}
>
<List
dataSource={files}
renderItem={item=> (
<List.Item
actions={[
// <a key="list-loadmore-edit">編輯</a>,
<a key="list-loadmore-delete" onClick={removeFile}>删除</a>
]}
// extra={ // }
key={item.path}>
<List.Item.Meta
avatar={
<>
{
!!item.type && ['image/gif', 'image/jpeg', 'image/png'].includes(item.type) &&
<img
width={100}
alt='logo'
src={item.preview}
/>
}
</>
}
title={item.path}
description={formatBytes(item.size)}
/>
</List.Item>
)}
>
{loading && hasMore && (
<div className="demo-loading-container">
<Spin />
</div>
)}
</List>
</InfiniteScroll>
</div>
</section>
);
}

flask代碼:

def multiplefiles():
if 'files' not in request.files:
return jsonify({'message': '沒有文件!'}), 200
files = request.files.getlist('files') for file in files:
if file:
# 通過拼音解决secure_filename中文問題
filename = secure_filename(''.join(lazy_pinyin(file.filename))
Path(UPLOAD_FOLDER + '/' + file_info['dir_path']).mkdir(parents=True, exist_ok=True)
file.save(os.path.join(UPLOAD_FOLDER + '/' + file_info['dir_path'], filename)) return jsonify({'message': '保存成功!!'})

2. 大文件導入:

通過file.slice()方法生成文件的chunks。不要用Promise.all容易產生非順序型的請求,導致文件損壞。

js代碼:

const promiseArray = largeFiles.map(file => new Promise((resolve, reject) => {
const chunkSize = CHUNK_SIZE;
const chunks = Math.ceil(file.size / chunkSize);
let chunk = 0;
let chunkArray = new Array();
while (chunk <= chunks) {
let offset = chunk * chunkSize;
let slice = file.slice(offset, offset+chunkSize)
chunkArray.push([slice, offset])
++chunk;
}
const chunkUploadPromises = (slice, offset) => {
const largeFileData = new FormData();
largeFileData.append('largeFileData', slice)
return new Promise((resolve, reject) => {
axios({
method: 'POST',
url: '/api/files/largefile',
data: largeFileData,
headers: {
"Content-Type": "multipart/form-data"
}
})
.then(resp => {
console.log(resp);
resolve(resp);
})
.catch(err => {
reject(err);
})
})
}; chunkArray.reduce( (previousPromise, [nextChunk, nextOffset]) => {
return previousPromise.then(() => {
return chunkUploadPromises(nextChunk, nextOffset);
});
}, Promise.resolve());
resolve();
}))

flask代碼:

filename = secure_filename(''.join(lazy_pinyin(filename)))
Path(UPLOAD_FOLDER + '/' + file_info['dir_path']).mkdir(parents=True, exist_ok=True)
save_path = os.path.join(UPLOAD_FOLDER + '/' + file_info['dir_path'], filename)
try:
with open(save_path, 'ab') as f:
f.seek(offset)
f.write(file.stream.read())
print("time: "+ str(datetime.now())+" offset: " + str(offset))
except OSError:
return jsonify({'Could not write to file'}), 500

結語

文件傳輸一直都是HTTP的痛點,尤其是大文件傳輸。最好的方式是自己做個Client,通過FTP和FTPS的協議進行傳輸。第二種來自於大廠很中心化的方法,通過文件的checksum來確定文件是否已經上傳了,來營造秒傳的效果。第三種來自去中心化的Bittorrent的方法每一個用戶做文件種子,提供文件傳輸的輔助,目前國內並沒有普及使用。

Python開發篇——基於React-Dropzone開發上傳組件的更多相關文章

  1. 基於SWFUpload的angular上傳組件

    回顧 由於工作內容比較多,特別是架構方面,需要耗費很多的時間調整.重構,因此很久沒有寫文章了. 話就不多說了,直接進入主題. 實現 首先分析一下SWFUpload初始化的時候,需要傳入當前觸發上傳的元 ...

  2. Vue.js 3.0搭配.NET Core寫一個牛B的文件上傳組件

    在開發Web應用程序中,文件上傳是經常用到的一個功能. 在Jquery時代,做上傳功能,一般找jQuery插件就够了,很少有人去探究上傳文件插件到底是怎麼做的. 簡單列一下我們要做的技術點和功能點 使 ...

  3. vue大文件上傳組件選哪個好?

    需求:項目要支持大文件上傳功能,經過討論,初步將文件上傳大小控制在500M內,因此自己需要在項目中進行文件上傳部分的調整和配置,自己將大小都以501M來進行限制. 第一步: 前端修改 由於項目使用的是 ...

  4. python 全棧開發,Day75(Django與Ajax,文件上傳,ajax發送json數據,基於Ajax的文件上傳,SweetAlert插件)

    昨日內容回顧 基於對象的跨錶查詢 正向查詢:關聯屬性在A錶中,所以A對象找關聯B錶數據,正向查詢 反向查詢:關聯屬性在A錶中,所以B對象找A對象,反向查詢 一對多: 按字段:xx book ----- ...

  5. java深入探究10-文件上傳組件FileUpload,郵件開發

    1.文件上傳組件FileUpload 1)java提供了文件上傳的工具包 需要引入:commons-fileupload-1.2.1.jar(文件上床組件核心包) commons-oi-1.4(封裝了 ...

  6. 【Java Web開發學習】Spring MVC文件上傳

    [Java Web開發學習]Spring MVC文件上傳 轉載:https://www.cnblogs.com/yangchongxing/p/9290489.html 文件上傳有兩種實現方式,都比較 ...

  7. 基於tornado python pandas和bootstrap上傳組件的mongodb數據添加工具

    總體思路:基於bootstrap4的前端頁面上傳組件,把excel文件上傳至服務器,並利用python pandas讀取裏面的數據形成字典列錶 通過pymongo 接口把數據插入或追加到mongodb ...

  8. Django與Ajax,文件上傳,ajax發送json數據,基於Ajax的文件上傳,SweetAlert插件

    一.Django與Ajax AJAX准備知識:JSON 什麼是 JSON ? JSON 指的是 JavaScript 對象錶示法(JavaScript Object Notation) JSON 是輕 ...

  9. 封裝react antd的upload上傳組件

    上傳文件也是我們在實際開發中常遇到的功能,比如上傳產品圖片以供更好地宣傳我們的產品,上傳excel文檔以便於更好地展示更多的產品信息,上傳zip文件以便於更好地收集一些資料信息等等.至於為何要把上傳組 ...

  10. 基於MVC4+EasyUI的Web開發框架形成之旅--附件上傳組件uploadify的使用

    大概一年前,我還在用Asp.NET開發一些行業管理系統的時候,就曾經使用這個組件作為文件的上傳操作,在隨筆<Web開發中的文件上傳組件uploadify的使用>中可以看到,Asp.NET中 ...

隨機推薦

  1. IOS網絡第三天 - 01-網絡文件下載(0922略)

    01 數據的安全01 - 密碼加密 02 數據的安全02 - 加密過程 01 -數據的安全01 - 本地存儲和代碼安全 04-網絡狀態監控 05-真機演示 06-小文件下載 07-大文件下載01-基本 ...

  2. HtmlAgilityPack組件

    HtmlAgilityPack組件用於解析Html字符串,一個典型的應用場景是用於網頁爬蟲. 示例程序 using Common.Tools; using Datebase.Entity; using ...

  3. (翻譯) TFS源代碼控制的未來 (TFSVC vs. Git)

    說明:由於博客園的限制,之前轉發的MVP盧建暉的文章不能放入首頁,但我會繼續轉發,感興趣的同學請到我的博客首頁查看. 博主: 翻譯自微軟Visual Studio ALM產品組老大Brian Harr ...

  4. EF 知識點

    EntityFrameWorak知識點記錄 發展史 EF1.0時,只支持Database First,數據庫優先.必須將設計器指向一個現有的數據庫. EF4時,支持Model First,模型優先.可 ...

  5. poj 1260 dp

    Description In Pearlania everybody is fond of pearls. One company, called The Royal Pearl, produces ...

  6. 【使用WCF,發布服務端瀏覽報錯】未能從程序集“System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089” 中加載類型 “System.ServiceModel.Activation.HttpModule”。

    問題: 在WIN7中的IIS服務器中部署WCF服務程序時,通過瀏覽器訪問報出如下錯誤: 未能從程序集"System.ServiceModel, Version=3.0.0.0, Cultur ...

  7. C語言實現循環隊列

    今日在處理數據存儲的問題中,數據占用的空間較大,在詢問之下,提及循環隊列. 沒有學習過的我,想想就是頭大,只能慢慢從網上找資料,一個字母一個字母的敲,最後,還是慢慢的對隊列有了一些理解 對於循環隊列有 ...

  8. osg shader 相機觀察矩陣逆矩陣 求頂點世界坐標

    uniform mat4 osg_ViewMatrixInverse;//osg內置uniform void main() { vec4 posWorld = osg_ViewMatrixInvers ...

  9. pip 國內源 配置

    pip 國內源 配置 2017年12月09日 16:05:20 閱讀數:183 最近使用 pip 安裝包,動輒十幾 k 甚至幾 k 的下載速度,確實讓人安裝的時候心情十分不好.所以還是要給 pip 換 ...

  10. SaltStack Pillar 詳解

    簡介 grains用於存儲靜態不易變更的數據,而pillar一般用於存儲動態, 敏感的數據,通過minion和master設置或獲取grains信息,而pillar信息只能在master端配置,在到m ...