人妖在线一区,国产日韩欧美一区二区综合在线,国产啪精品视频网站免费,欧美内射深插日本少妇

新聞動(dòng)態(tài)

Python基于React-Dropzone實(shí)現(xiàn)上傳組件的示例代碼

發(fā)布日期:2022-02-11 16:50 | 文章來(lái)源:腳本之家

這次我要講述的是在React-Flask框架上開(kāi)發(fā)上傳組件的技巧。我目前主要以React開(kāi)發(fā)前端,在這個(gè)過(guò)程中認(rèn)識(shí)到了許多有趣的前端UI框架——React-Bootstrap、Ant Design、Material UI、Bulma等。而比較流行的上傳組件也不少,而目前用戶比較多的是jQuery-File-Upload和Dropzone,而成長(zhǎng)速度快的新晉有Uppy和filepond。比較惋惜的是Fine-Uploader的作者自2018年后就決定不再維護(hù)了,原因作為后來(lái)者的我就不多過(guò)問(wèn)了,但請(qǐng)各位尊重每一位開(kāi)源作者的勞動(dòng)成果。

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

  • 基于React開(kāi)發(fā),契合度高
  • 網(wǎng)上推薦度高,連Material UI都用他開(kāi)發(fā)上傳組件
  • 主要以 Drag 和 Drop 為主,但是對(duì)于傳輸邏輯可以由開(kāi)發(fā)者自行設(shè)計(jì)。例如嘗試用socket-io來(lái)傳輸file chunks。對(duì)于node全棧估計(jì)可行,但是我這里使用的是Flask,需要將Blob轉(zhuǎn)ArrayBuffer。但是如何將其在Python中讀寫(xiě),我就沒(méi)進(jìn)行下去了。

實(shí)例演示

1. axios上傳普通文件:

通過(guò)yarn將react-dropzone和引入:

yarn add react-dropzone axios

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

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';
/**
* 計(jì)算文件大小
* @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>拖動(dòng)文件或點(diǎn)擊選擇文件😊</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': '沒(méi)有文件!'}), 200
files = request.files.getlist('files')
for file in files:
 if file:
  # 通過(guò)拼音解決secure_filename中文問(wèn)題
  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. 大文件導(dǎo)入:

通過(guò)file.slice()方法生成文件的chunks。不要用Promise.all容易產(chǎn)生非順序型的請(qǐng)求,導(dǎo)致文件損壞。

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)
# rm file if exists
if offset == 0 and save_path.exists(filename):
 os.remove(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

結(jié)語(yǔ)

文件傳輸一直都是HTTP的痛點(diǎn),尤其是大文件傳輸。最好的方式是自己做個(gè)Client,通過(guò)FTP和FTPS的協(xié)議進(jìn)行傳輸。第二種來(lái)自于大廠很中心化的方法,通過(guò)文件的checksum來(lái)確定文件是否已經(jīng)上傳了,來(lái)營(yíng)造秒傳的效果。第三種來(lái)自去中心化的Bittorrent的方法每一個(gè)用戶做文件種子,提供文件傳輸?shù)妮o助,目前國(guó)內(nèi)并沒(méi)有普及使用。

到此這篇關(guān)于Python基于React-Dropzone實(shí)現(xiàn)上傳組件的示例代碼的文章就介紹到這了,更多相關(guān)Python React-Dropzone上傳組件內(nèi)容請(qǐng)搜索本站以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持本站!

香港穩(wěn)定服務(wù)器

版權(quán)聲明:本站文章來(lái)源標(biāo)注為YINGSOO的內(nèi)容版權(quán)均為本站所有,歡迎引用、轉(zhuǎn)載,請(qǐng)保持原文完整并注明來(lái)源及原文鏈接。禁止復(fù)制或仿造本網(wǎng)站,禁止在非www.sddonglingsh.com所屬的服務(wù)器上建立鏡像,否則將依法追究法律責(zé)任。本站部分內(nèi)容來(lái)源于網(wǎng)友推薦、互聯(lián)網(wǎng)收集整理而來(lái),僅供學(xué)習(xí)參考,不代表本站立場(chǎng),如有內(nèi)容涉嫌侵權(quán),請(qǐng)聯(lián)系alex-e#qq.com處理。

相關(guān)文章

實(shí)時(shí)開(kāi)通

自選配置、實(shí)時(shí)開(kāi)通

免備案

全球線路精選!

全天候客戶服務(wù)

7x24全年不間斷在線

專屬顧問(wèn)服務(wù)

1對(duì)1客戶咨詢顧問(wèn)

在線
客服

在線客服:7*24小時(shí)在線

客服
熱線

400-630-3752
7*24小時(shí)客服服務(wù)熱線

關(guān)注
微信

關(guān)注官方微信
頂部