這次我要講述的是在React-Flask框架上開發上傳組件的技巧。我目前主要以React開發前端,在這個過程中認識到了許多有趣的前端UI框架——React-Bootstrap、Ant Design、Material UI、Bulma等。而比較流行的上傳組件也不少,而目前用戶比較多的是 jQuery-File-Upload和Dropzone,而成長速度快的新晋有Uppy和filepond。比較惋惜的是Fine-Uploader的作者自2018年後就决定不再維護了,原因作為後來者的我就不多過問了,但請各比特尊重每一比特開源作者的勞動成果。
這裏我選擇React-Dropzone,原因如下:
- 基於React開發,契合度高
- 網上推薦度高,連Material UI都用他開發上傳組件
- 主要以
Drag
和Drop
為主,但是對於傳輸邏輯可以由開發者自行設計。例如嘗試用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開發上傳組件的更多相關文章
- 基於SWFUpload的angular上傳組件
回顧 由於工作內容比較多,特別是架構方面,需要耗費很多的時間調整.重構,因此很久沒有寫文章了. 話就不多說了,直接進入主題. 實現 首先分析一下SWFUpload初始化的時候,需要傳入當前觸發上傳的元 ...
- Vue.js 3.0搭配.NET Core寫一個牛B的文件上傳組件
在開發Web應用程序中,文件上傳是經常用到的一個功能. 在Jquery時代,做上傳功能,一般找jQuery插件就够了,很少有人去探究上傳文件插件到底是怎麼做的. 簡單列一下我們要做的技術點和功能點 使 ...
- vue大文件上傳組件選哪個好?
需求:項目要支持大文件上傳功能,經過討論,初步將文件上傳大小控制在500M內,因此自己需要在項目中進行文件上傳部分的調整和配置,自己將大小都以501M來進行限制. 第一步: 前端修改 由於項目使用的是 ...
- python 全棧開發,Day75(Django與Ajax,文件上傳,ajax發送json數據,基於Ajax的文件上傳,SweetAlert插件)
昨日內容回顧 基於對象的跨錶查詢 正向查詢:關聯屬性在A錶中,所以A對象找關聯B錶數據,正向查詢 反向查詢:關聯屬性在A錶中,所以B對象找A對象,反向查詢 一對多: 按字段:xx book ----- ...
- java深入探究10-文件上傳組件FileUpload,郵件開發
1.文件上傳組件FileUpload 1)java提供了文件上傳的工具包 需要引入:commons-fileupload-1.2.1.jar(文件上床組件核心包) commons-oi-1.4(封裝了 ...
- 【Java Web開發學習】Spring MVC文件上傳
[Java Web開發學習]Spring MVC文件上傳 轉載:https://www.cnblogs.com/yangchongxing/p/9290489.html 文件上傳有兩種實現方式,都比較 ...
- 基於tornado python pandas和bootstrap上傳組件的mongodb數據添加工具
總體思路:基於bootstrap4的前端頁面上傳組件,把excel文件上傳至服務器,並利用python pandas讀取裏面的數據形成字典列錶 通過pymongo 接口把數據插入或追加到mongodb ...
- Django與Ajax,文件上傳,ajax發送json數據,基於Ajax的文件上傳,SweetAlert插件
一.Django與Ajax AJAX准備知識:JSON 什麼是 JSON ? JSON 指的是 JavaScript 對象錶示法(JavaScript Object Notation) JSON 是輕 ...
- 封裝react antd的upload上傳組件
上傳文件也是我們在實際開發中常遇到的功能,比如上傳產品圖片以供更好地宣傳我們的產品,上傳excel文檔以便於更好地展示更多的產品信息,上傳zip文件以便於更好地收集一些資料信息等等.至於為何要把上傳組 ...
- 基於MVC4+EasyUI的Web開發框架形成之旅--附件上傳組件uploadify的使用
大概一年前,我還在用Asp.NET開發一些行業管理系統的時候,就曾經使用這個組件作為文件的上傳操作,在隨筆<Web開發中的文件上傳組件uploadify的使用>中可以看到,Asp.NET中 ...
隨機推薦
- IOS網絡第三天 - 01-網絡文件下載(0922略)
01 數據的安全01 - 密碼加密 02 數據的安全02 - 加密過程 01 -數據的安全01 - 本地存儲和代碼安全 04-網絡狀態監控 05-真機演示 06-小文件下載 07-大文件下載01-基本 ...
- HtmlAgilityPack組件
HtmlAgilityPack組件用於解析Html字符串,一個典型的應用場景是用於網頁爬蟲. 示例程序 using Common.Tools; using Datebase.Entity; using ...
- (翻譯) TFS源代碼控制的未來 (TFSVC vs. Git)
說明:由於博客園的限制,之前轉發的MVP盧建暉的文章不能放入首頁,但我會繼續轉發,感興趣的同學請到我的博客首頁查看. 博主: 翻譯自微軟Visual Studio ALM產品組老大Brian Harr ...
- EF 知識點
EntityFrameWorak知識點記錄 發展史 EF1.0時,只支持Database First,數據庫優先.必須將設計器指向一個現有的數據庫. EF4時,支持Model First,模型優先.可 ...
- poj 1260 dp
Description In Pearlania everybody is fond of pearls. One company, called The Royal Pearl, produces ...
- 【使用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 ...
- C語言實現循環隊列
今日在處理數據存儲的問題中,數據占用的空間較大,在詢問之下,提及循環隊列. 沒有學習過的我,想想就是頭大,只能慢慢從網上找資料,一個字母一個字母的敲,最後,還是慢慢的對隊列有了一些理解 對於循環隊列有 ...
- osg shader 相機觀察矩陣逆矩陣 求頂點世界坐標
uniform mat4 osg_ViewMatrixInverse;//osg內置uniform void main() { vec4 posWorld = osg_ViewMatrixInvers ...
- pip 國內源 配置
pip 國內源 配置 2017年12月09日 16:05:20 閱讀數:183 最近使用 pip 安裝包,動輒十幾 k 甚至幾 k 的下載速度,確實讓人安裝的時候心情十分不好.所以還是要給 pip 換 ...
- SaltStack Pillar 詳解
簡介 grains用於存儲靜態不易變更的數據,而pillar一般用於存儲動態, 敏感的數據,通過minion和master設置或獲取grains信息,而pillar信息只能在master端配置,在到m ...