Axios upload fisier de mari dimensiuni

(Stanciu Bogdan Mircea) #1

Pe local rulez o instanță de React și una de Flask. Încerc să uploadez din React un fișier folosind axios pe un endoint din Flask.

Sunt gramadă de lecții pentru asta, în mare se reduce la un form și un event de tipul:

handleUploadImage(ev) {
    ev.preventDefault();

    const data = new FormData();
    data.append('file', this.uploadInput.files[0]);
    data.append('filename', this.fileName.value);

    axios.post('http://localhost:8000/upload', data)
      .then(function (response) {
    this.setState({ imageURL: `http://localhost:8000/${body.file}`, uploadStatus: true });
      })
      .catch(function (error) {
        console.log(error);
      });
  }

Asta am înțeles însă sunt curios dacă modelul este suficient și pentru transferul fișierelor de dimensiuni mari. Pot transmite astfel fișiere video cu dimensiuni de ordinul GB? Dacă da, este aceasta metoda recomandată?

(Eduard-Dan Stanescu) #2

De ce nu ar fi ok ?
Nu uita de timeout: 99999   :troll:

(Victor) #3

In general problemele cu fisierele mari nu vin de la front-end ci mai degraba de la back-end + setarile serverului http.

2 Likes
(cosmos) #4

S-ar putea sa fie util.

(Stanciu Bogdan Mircea) #5

Poate găsesc ceva util în răspunsul lui Cosmin. Problema este că un search de tipul javascript upload large files aduce doar rezultate pentru un upload a unui .jpeg sau .png.

Nu am încercat să aplic metoda de mai sus și pentru fișiere mari dar intuiția îmi spune că oricum nu ar fi o idee bună.

(victor) #6

Incearca Chunking file uploads

(Adrian Tiberius) #7
3 Likes
(George Calianu) #8

Daca nu are mare importanta tehnologia poate iti este util acest proiect care transfera fisiere mari. Intern, pe Linux de exemplu, transfera in /tmp apoi face copy and delete in locatia finala. Asta inseamna ca cere spatiu dublu fata de dimensiunea fisierului transferat si ceva mai mult timp pentru copiere dar isi face bine treaba. Ultima data am transferat un fisier de test cam de 60.5GB insa poate si mai mult.

1 Like
(Stanciu Bogdan Mircea) #9

Termenul de resumable m-a ajutat sa gasesc ceva soluții interesante.

  • uppy - un file uploader open-source ce acoperă si clientul si serverul
  • protocolul TUS, pentru care am găsit și wrapper de python

Ce trebuia eu să înțeleg este că și back-end-ul trebuie pregătit special pentru transfer de fișiere mari. Mulțumesc celor ce au răspuns, lucrurile îmi sunt mult mai clare acum.

@Cosmin_Popescu, articolul ăla va fi util când am să ies din localhost

4 Likes
(cosmos) #10

Ma bucur ca te-a ajutat. :slight_smile:
Acum ceva timp al dat de asta
https://tus.io

Conceptul de resumable uploads mi se pare interesant. Au pe site si un demo unde il poti testa + biblioteci si clienti pt cam toate limbajele

1 Like
(Stanciu Bogdan Mircea) #11

Am reusit sa fac un upload. Las aici niște cod în caz că mai dă careva peste articol în viitor.

Pe client am folosit Uppy pentru react. După inițializarea lui Uppy adaug și pluginul TUS cu link către endpointul flask.

import React from 'react'
const Uppy = require('@uppy/core')
const Tus = require('@uppy/tus')
import Dashboard from '@uppy/react/lib/Dashboard'

export default class UppyComp extends React.Component {
    constructor(props) {
        super(props)
        this.uppy = new Uppy({
            meta: { type: 'avatar' },
            restrictions: { maxNumberOfFiles: 1 },
            autoProceed: true,
            debug: true, 
        })
        .use(Tus, { endpoint: 'http://192.xxx.xxx.xx:5000/file-upload' })
        .run()
    }

  render() {
    return (
        <Dashboard
            uppy={this.uppy}
        />
    )
  }

Server side am instalat https://github.com/bratao/tus-flask. Este un fork a unui proiect nemenținut ce apare ca recomandare pe pagina oficială TUS.

Exemplul oferit de readme este suficient doar că eu m-am lovit de protocolul CORS. Am scris un wrapper pentru clasa din packetul instalat ca să pot adăuga headerele cerute pentru transfer. Intuiesc că nu este ok pentru producție, dar măcar am un P.O.C. .

def upload_resumable(tmpfile):
    print('done')
    return 'End of upload'


class TusWrapped(TusFilter):
    def __init__(self, app, upload_path, api_base='', tmp_dir='/tmp/upload', expire=60 * 60 * 24 * 30, send_file=False,
                 max_size=2 ** 31, callback=None):
        super().__init__(app, upload_path, api_base=api_base, tmp_dir=tmp_dir, expire=expire, send_file=send_file, max_size=max_size, callback=callback)

    def __call__(self, environ, start_response):
        req = webob.Request(environ)

        resp = webob.Response()
        resp.headers.add('Access-Control-Allow-Origin', req.headers.environ.get('HTTP_ORIGIN'))
        resp.headers.add('Access-Control-Allow-Headers', '*')
        resp.headers.add('Access-Control-Expose-Headers', 'Location, Upload-Length, Upload-Offset')
        resp.headers.add('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS')

        temp = dict(upload_finished=False, info_loaded=False)
        info = dict()
        env = Env(req=req, resp=resp, temp=temp, info=info)
        if not req.path.startswith(self.upload_path):
            return self.app(environ, start_response)
        try:
            self.handle(env)
        except Error as e:
            self.finish_error(env, e)

        if env.temp['upload_finished'] and (self.callback is not None):
            self.callback(env.temp['uid'])

        return resp(environ, start_response)


app.wsgi_app = TusWrapped(
    app.wsgi_app,
    upload_path='/file-upload',
    tmp_dir='tmp',
    callback=upload_resumable
)

Mă gândesc că ar fi o idee bună să separ uploadul de fișiere de restul API-ului și dacă aș face asta (prin containere de docker dedicate) poate folosesc serverul node menținut chiar de organizația TUS

1 Like
(Stanciu Bogdan Mircea) #12

Nu m-am uitat la toate dar:

  • pe frontend arată bine
  • pentru server implementarea lor de Node are suport bun iar pe partea de Python este jale :frowning:
1 Like