Skip to content

Commit 4e9ab39

Browse files
adamzielclaude
andcommitted
Fix partial file download loss on multi-chunk resume
When a large file spans multiple HTTP requests, continuation chunks arrive with is_first=false. But the StreamingContext resets between requests, leaving file_handle=null, so the chunk handler silently drops the remaining data. The importer then reports success with a truncated file. Fix: if a partially downloaded file is tracked in state, re-open it in append mode before streaming begins so continuation chunks are written correctly. Also give the large-single-file E2E test its own dedicated site to prevent interference from test-28's concurrent-error hooks which modify files on the shared import-failures site. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 7dde891 commit 4e9ab39

File tree

3 files changed

+23
-2
lines changed

3 files changed

+23
-2
lines changed

importer/import.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2768,6 +2768,26 @@ private function download_file_fetch(
27682768
$context->file_path = null;
27692769
$context->file_ctime = null;
27702770

2771+
// Resume recovery: if a file was partially downloaded in a previous
2772+
// request, re-open it in append mode so continuation chunks (where
2773+
// is_first=false) can still be written. Without this, the context
2774+
// starts with file_handle=null and non-first chunks are silently dropped.
2775+
if ($tracked_file !== null && $tracked_bytes !== null && file_exists($tracked_file)) {
2776+
$context->file_handle = fopen($tracked_file, "ab");
2777+
if ($context->file_handle) {
2778+
$context->file_path = $tracked_file;
2779+
$context->file_bytes_written = $tracked_bytes;
2780+
$this->audit_log(
2781+
sprintf(
2782+
"RESUME FILE | Re-opened %s at %d bytes for continued download",
2783+
$tracked_file,
2784+
$tracked_bytes,
2785+
),
2786+
true,
2787+
);
2788+
}
2789+
}
2790+
27712791
$context->on_chunk = function ($chunk) use (
27722792
&$cursor,
27732793
&$complete,

tests/e2e/site-registry.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"error-chunks": { "port": 8099 },
2727
"import-failures": { "port": 8100 },
2828
"follow-symlinks": { "port": 8101 },
29-
"sqlite": { "port": 8102 }
29+
"sqlite": { "port": 8102 },
30+
"large-single-file": { "port": 8103 }
3031
}
3132
}

tests/e2e/tests/import-24-large-single-file.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
import { ensureSite } from '../lib/site-setup.js';
1818

1919
describe('Import: Large Single File', { timeout: 180000 }, () => {
20-
const site = 'import-failures';
20+
const site = 'large-single-file';
2121
let tempDir;
2222
const largeName = 'test-data/large-12mb.bin';
2323

0 commit comments

Comments
 (0)