Skip to content

feat: add FileDownloadCrawler#2043

Open
Mantisus wants to merge 1 commit into
apify:masterfrom
Mantisus:file-downloaw-crawler
Open

feat: add FileDownloadCrawler#2043
Mantisus wants to merge 1 commit into
apify:masterfrom
Mantisus:file-downloaw-crawler

Conversation

@Mantisus

@Mantisus Mantisus commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Description

  • Adds FileDownloadCrawler with support for streaming the response body in the request handler.

Issues

Testing

  • Added tests for FileDownloadCrawler

@Mantisus Mantisus self-assigned this Jul 8, 2026
@Mantisus Mantisus force-pushed the file-downloaw-crawler branch from 4669391 to db4ded0 Compare July 10, 2026 10:41
@Mantisus Mantisus marked this pull request as ready for review July 10, 2026 10:48
@Mantisus Mantisus requested review from Pijukatel and vdusek July 10, 2026 10:48

@Pijukatel Pijukatel left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For large files, it would be great to have something like https://pypi.org/project/smart-open/ , but our storages lacks interface to support such chunked uploads

Comment thread docs/guides/http_crawlers.mdx

@vdusek vdusek left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otherwise it looks great

)

self._statistics.register_status_code(response.status_code)
context.request.state = RequestState.AFTER_NAV

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From Claude:

The streamed path never sets context.request.loaded_url, whereas the buffered crawl() path does (request.loaded_url = str(response.url)). As a result, in stream mode _check_url_after_redirects is effectively skipped (it's guarded by if loaded_url is not None), so off-domain redirects aren't filtered by the enqueue strategy, and context.request.loaded_url stays None for user code. It's a silent divergence between the two modes. The stream() API doesn't expose the final URL, so this may be hard to fix cleanly, but worth a note/follow-up.

# been read yet.
crawler = FileDownloadCrawler(
stream=True,
# The streamed request runs under this timeout, so raise it for large downloads.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude's nit:

In stream mode navigation_timeout only bounds connection establishment + headers, not the body download (that runs under request_handler_timeout, as the next comment correctly says). This wording reads as if the whole download is bounded by navigation_timeout, so a reader tuning only this value for a large file would still hit the handler timeout.

content = await context.http_response.read()
kvs = await context.get_key_value_store()
await kvs.set_value(
key=context.request.url.split('/')[-1],

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude's nit:

context.request.url.split('/')[-1] yields '' for a trailing-slash URL, and kvs.set_value(key='') would raise. The runnable example (file_download.py) uses the safer URL(context.request.url).name — worth matching here (and at line 73) so the docstring doesn't teach the more fragile pattern.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement a FileDownload crawler

4 participants