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