fohrloop / dash-uploader

The alternative upload component for python Dash applications.
MIT License
144 stars 30 forks source link

Serving app on different path? #24

Closed sorenwacker closed 4 months ago

sorenwacker commented 3 years ago

I am serving my not on / but on something like sub1.example.org/mypath. Dash has an option for that are requests_pathname_prefix and routes_pathname_prefix. I found that uploader does not take these settings into account and therefore breaks as it looks on /. Is there a setting to change that?

fohrloop commented 3 years ago

Interesting. There were previously problems if requests_pathname_prefix and/or routes_pathname_prefix were used, as discussed in https://github.com/np-8/dash-uploader/issues/3, and it these were fixed in v.0.3.0. Then, there were issues if url_base_pathname was defined in dash.Dash() (See: https://github.com/np-8/dash-uploader/issues/15). I think I got those fixed in v.0.4.0., and I hope the changes did not break anything with requests_pathname_prefix and routes_pathname_prefix as they are all somehow connected.

These are actually quite difficult to test since I am only developing on my local machine. Could you test with both v.0.3.0 and v.0.4.1, and report back how it goes? Include your url_base_pathname, routes_pathname_prefix and requests_pathname_prefix from your app.config, the URL that the app points the HTTP POST requests (using browser devtools), e.g.

http://127.0.0.1:8050/API/resumable?resumableChunkNumber=1&resumableChunkSize=1048576&resumableCurrentChunkSize=74&resumableTotalSize=74&resumableType=&resumableIdentifier=74-testmd&resumableFilename=test.md&resumableRelativePath=test.md&resumableTotalChunks=1&upload_id=0c67b705-187b-11eb-b5a4-2016b9d15494

and the expected/desired URL for the HTTP POST requests.

If we can create a minimal reproducible example that replicates the issue we probably can hunt it down quite fast.

fohrloop commented 3 years ago

And to add to the other part of your question, dash-uploader should take the abovementioned settings into account automatically (since it has access to app.config, and users should not need to worry about them).

cainmagi commented 3 years ago

I meet the same problem when trying to deploy the service on a different server.

In my tries, I hack into the python source codes and make some changes to the du.Upload. The service option is exposed directly. For example, my dashboard is deployed on http://192.168.2.18:8060, but I want to upload the files to another service (:8061). Then I writes codes for the dashboard (8060) side:

du.Upload(
    id='uploader',
    text='Drag & Drop or Select a File',
    filetypes=None,
    upload_id='.',
    max_files=1,
    max_file_size=20 * 1024,
    service_addr='http://192.168.2.18:8061/API/resumable',
    default_style={
        'width': '100%',
        'paddingLeft': '.5rem',
        'paddingRight': '.5rem',
        'minHeight': 'min-content',
        'lineHeight': '50px',
        'borderWidth': '1px',
        'borderStyle': 'dashed',
        'borderRadius': '5px',
        'textAlign': 'center'
    }
)

On the service side (8061), I use some tricks like this:

import os
import flask

from dash_uploader.configure_upload import decorate_server as du_service

app = flask.Flask('test-uploader')

os.makedirs('upload', exist_ok=True)
du_service(app, temp_base='upload', upload_api='/API/resumable', use_upload_id=False)

if __name__ == '__main__':
    import colorama
    import termcolor

    colorama.init()
    app.run(host='192.168.2.18', port=8061, debug=False)

When I start both services, I find an interesting thing. If the Upload is deployed for the same service (I mean the service option is not overwritten), the files could be uploaded successfully, the logs should be like this:

INFO:wsgi:::1 - - [21/Apr/2021:16:16:59 -0500] "POST /API/resumable?resumableChunkNumber=1&resumableChunkSize=5242880&resumableCurrentChunkSize=3956920&resumableTotalSize=3956920&resumableType=application%2Fx-gzip&resumableIdentifier=3956920-yjin-dlis-validator-v13targz&resumableFilename=example.tar.gz&resumableRelativePath=example.tar.gz&resumableTotalChunks=1&upload_id=. HTTP/1.1" 200 31 "http://localhost:8060/page-upload" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.72 Safari/537.36"

However, If I provide the service option, the file could not be uploaded. From the service side, I could see some requests, but their method is not POST but OPTIONS,

192.168.2.18 - - [21/Apr/2021 16:07:56] "OPTIONS /API/resumable?resumableChunkNumber=1&resumableChunkSize=1048576&resumableCurrentChunkSize=1048576&resumableTotalSize=3987136&resumableType=application%2Fx-zip-compressed&resumableIdentifier=3987136-yjin-dlis-validator-v13zip&resumableFilename=example.zip&resumableRelativePath=example.zip&resumableTotalChunks=3&upload_id=. HTTP/1.1" 200 -

The same OPTIONS requests are transmitted several times, then the uploading freezes. I had checked the documentation of resumablejs and wanted to figure out why the method is OPTIONS, but I could not find anything useful. Does the author know why the uploader behaves like this? I think it may be a key step for handling the request method problem.

Thank you!

fohrloop commented 3 years ago

Hi @cainmagi,

Interesting approach. What is lacking from the dash-uploader which forces you to "hack" the code?

There is du.configure_upload() which is supposed to configure the correct upload API for you. It also has an upload_api argument. Could that help you?

cainmagi commented 3 years ago

Hi @np-8,

Because I only want the dash-uploader served for my flask app, not dash app. The uploader component is deployed for another dash app. By the way, I hack the dash-uploader because I need to provide several different uploaders pointed to different services, including the local dashboard service and some remote flask services. To perform the test, I add an argument service_addr to the du.Upload. It just simply exposes the argument service of Upload_ReactComponent. If this service_addr is None, it will fall back to the default configuration of du.Upload.

sorenwacker commented 3 years ago

That sounds very useful. I can try testing it.

fohrloop commented 3 years ago

@cainmagi

Oh yeah, did not think about the option that someone might be interested using the component in Flask application. The another question would be to make it possible to have several dash-uploader components working on a same site at the same time. This will require some work for sure.

I don't have time to solve this now myself, but I would be really happy to merge a PR or hear about your solution(s). I guess it would be best to try to solve one problem at a time: either using one dash-uploader component inside the Flask application or using multiple dash-uploader components inside a Dash application. Once both problems are solved separately, merging them might be much earier.

For the HTTP OPTIONS requests you see, I have no clue either. Perhaps this SO question is helpful. The component should normally currently use HTTP POST requests.

I just finished some refactoring & updates for the dash-uploader, and uploaded v.0.5.0 to PyPI. Now it is possible to configure handling of POST and GET requests with a du.HttpRequestHandler. I'm not sure if it directly helps you but if you are going to make changes to the dash-uploader core, perhaps now it is a little bit easier.

cainmagi commented 3 years ago

@np-8

Thank you! Your reference is useful. I will try to implement this feature this week. I will try to start my work from 0.4.x version. If successful, I will try to migrate to 0.5.0 version. By the way, I am not a professional coder about web development, so I could not promise that I could work it out. But I will try my best to do it.


Edit:

Just now I found that you have already published 0.5.0, so I will work on it.

cainmagi commented 3 years ago

@np-8 @Vipulsh

Hello!

I have created my pull request for solving this issue. See #36 for checking details. Because I am not an expert in web development, please help me check the codes for bug fixing. Thank you!