Skip to content

Commit 6f4acab

Browse files
committed
FileResponse: accept BufferedReader
1 parent 8bf5d4d commit 6f4acab

File tree

1 file changed

+25
-10
lines changed

1 file changed

+25
-10
lines changed

aiohttp/web_fileresponse.py

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,19 @@ class FileResponse(StreamResponse):
9090

9191
def __init__(
9292
self,
93-
path: PathLike,
93+
path: PathLike | io.BufferedReader,
9494
chunk_size: int = 256 * 1024,
9595
status: int = 200,
9696
reason: Optional[str] = None,
97-
headers: Optional[LooseHeaders] = None,
97+
headers: Optional[LooseHeaders] = None
9898
) -> None:
9999
super().__init__(status=status, reason=reason, headers=headers)
100-
101-
self._path = pathlib.Path(path)
100+
if isinstance(path, io.BufferedReader):
101+
self._io = path
102+
self._path = None
103+
else:
104+
self._io = None
105+
self._path = pathlib.Path(path)
102106
self._chunk_size = chunk_size
103107

104108
def _seek_and_read(self, fobj: IO[Any], offset: int, chunk_size: int) -> bytes:
@@ -189,8 +193,6 @@ def _make_response(
189193
file_path, st, file_encoding = self._get_file_path_stat_encoding(
190194
accept_encoding
191195
)
192-
if not file_path:
193-
return _FileResponseResult.NOT_ACCEPTABLE, None, st, None
194196

195197
etag_value = f"{st.st_mtime_ns:x}-{st.st_size:x}"
196198

@@ -220,6 +222,12 @@ def _make_response(
220222
):
221223
return _FileResponseResult.NOT_MODIFIED, None, st, file_encoding
222224

225+
if file_path is None:
226+
if self._io is None:
227+
return _FileResponseResult.NOT_ACCEPTABLE, None, st, None
228+
229+
return _FileResponseResult.SEND_FILE, self._io, st, file_encoding
230+
223231
fobj = file_path.open("rb")
224232
with suppress(OSError):
225233
# fstat() may not be available on all platforms
@@ -232,6 +240,10 @@ def _make_response(
232240
def _get_file_path_stat_encoding(
233241
self, accept_encoding: str
234242
) -> Tuple[Optional[pathlib.Path], os.stat_result, Optional[str]]:
243+
if self._path is None:
244+
assert self._io is not None
245+
return None, os.stat(self._io.fileno()), None
246+
235247
file_path = self._path
236248
for file_extension, file_encoding in ENCODING_EXTENSIONS.items():
237249
if file_encoding not in accept_encoding:
@@ -377,11 +389,14 @@ async def _prepare_open_file(
377389
# extension of the request path. The encoding returned by guess_type
378390
# can be ignored since the map was cleared above.
379391
if hdrs.CONTENT_TYPE not in self._headers:
380-
if sys.version_info >= (3, 13):
381-
guesser = CONTENT_TYPES.guess_file_type
392+
if self._path is not None:
393+
if sys.version_info >= (3, 13):
394+
guesser = CONTENT_TYPES.guess_file_type
395+
else:
396+
guesser = CONTENT_TYPES.guess_type
397+
self.content_type = guesser(self._path)[0] or FALLBACK_CONTENT_TYPE
382398
else:
383-
guesser = CONTENT_TYPES.guess_type
384-
self.content_type = guesser(self._path)[0] or FALLBACK_CONTENT_TYPE
399+
self.content_type = FALLBACK_CONTENT_TYPE
385400

386401
if file_encoding:
387402
self._headers[hdrs.CONTENT_ENCODING] = file_encoding

0 commit comments

Comments
 (0)