Describe the bug
I want to expose api endpoint from which I can download static directory. I get 2 unexpected behaviors:
0.0.0.0/static I see No operations defined in spec!raise RuntimeError(f"Directory '{directory}' does not exist").This is really important for my home project to make sense so any input is really appreciated because I'm stuck on this.
To Reproduce
Code:
from starlette.staticfiles import StaticFiles
from fastapi import FastAPI
app = FastAPI(title="static_api")
app.mount("/static", StaticFiles(directory="static"))
Dockerfile:
FROM python:3.7
RUN pip install aiofiles fastapi uvicorn python-multipart
EXPOSE 80
COPY ./app /app
COPY ./static /static
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
I use docker build -t myimage . and docker run -p 80:80 myimage.
Expected behavior
Download static directory(or at least any file from this dir) from 0.0.0.0/static
Environment:
Docker above.
Already viewed and tried issues/tutorials
https://fastapi.tiangolo.com/tutorial/static-files/
https://www.starlette.io/staticfiles/
https://github.com/tiangolo/fastapi/issues/130
Even if this answer comes a little late, maybe it helps others, because I got into the same problem in the beginning.
I am not sure if this is a bug either?
Because it's called static file and not static directory.
So you won't be able to request a folder, only a file, which has to be explicit.
0.0.0.0/static/existing_file
if you want to get files as folder you could zip your files to one file
0.0.0.0/static/existing_files.zip
If the error result is that the folder was not found, then a wrong specification must have happened. The following should run, if you want static inside you app-folder:
from starlette.staticfiles import StaticFiles
from fastapi import FastAPI
app = FastAPI(title="static_api")
app.mount("/static", StaticFiles(directory="app/static"))
So you don't need to copy the static folder in dockerfile additionaly.
this works fine on docker but when you try to run the main.py it says the folder does not exist
Can you share the full stack trace? In particular, is the error coming from StaticFiles or somewhere downstream?
If you are just getting raise RuntimeError(f"Directory '{directory}' does not exist") then this should be easy enough to solve by just getting the directory right. You might need to use an absolute path somewhere that you are currently using a relative path, or vice versa.
I would recommend trying to run in a debugger and investigating if you can figure out where it is looking / why it can't find the directory at the point where the error is occurring. If you ultimately find an inconsistency between the runtime behavior and the FastAPI and/or starlette docs, please let us know.
Edit: I see this issue is actually rather old; I'm assuming the original issue has been addressed (there have certainly been some updates to starlette since this issue was opened that have fixed issues with serving static files).
@yaronv if you are still running into issues, the first part of the comment may still be relevant to you.
Ok, Thanks, I understand the solution above.
It's just that It's annoying to use absolute path because the path is different between the local machine and the docker environment.
Never mind, I will use some configs for that.
Thanks!
@yaronv you might be able to pass absolute paths without needing to actually hard code an absolute path by doing something like:
from pathlib import Path
current_file = Path(__file__)
current_file_dir = current_file.parent
project_root = current_file_dir.parent
project_root_absolute = project_root.resolve()
static_root_absolute = project_root_absolute / "static" # or wherever the static folder actually is
Obviously you can reduce the number of lines involved, I just made it more verbose to make it clear what was going on.
Or you could also use an environment variable/config as you noted.
Thanks @dmontagu for the help here!
I think that should answer the question, right @MLman99 ?
Assuming the original issue was solved, it will be automatically closed now. But feel free to add more comments or create new issues.
Most helpful comment
@yaronv you might be able to pass absolute paths without needing to actually hard code an absolute path by doing something like:
Obviously you can reduce the number of lines involved, I just made it more verbose to make it clear what was going on.
Or you could also use an environment variable/config as you noted.