Replace ZipStream with native ZipArchive for FrankenPHP compatibility
ZipStream writes via fwrite(php://output) which FrankenPHP silently drops, resulting in 0-byte ZIP downloads. Switch to ZipArchive to build the ZIP as a temp file on disk, then serve with response()->download(). - Rewrite download() to use ZipArchive + BinaryFileResponse - Remove decryptFileToCallback() from FileEncryptionService - Update tests for BinaryFileResponse instead of StreamedResponse - Remove maennchen/zipstream-php dependency Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
e37b322dde
commit
729e01462b
@@ -86,6 +86,7 @@ test('download counter increments on zip download', function () {
|
||||
|
||||
$encryptionService = app(FileEncryptionService::class);
|
||||
$key = $share->encryption_key;
|
||||
$content = 'test content';
|
||||
|
||||
foreach ($share->files as $file) {
|
||||
$dir = Storage::disk('shares')->path($share->token);
|
||||
@@ -94,17 +95,89 @@ test('download counter increments on zip download', function () {
|
||||
}
|
||||
$encryptedPath = $dir.'/'.basename($file->stored_path);
|
||||
$tempSource = tempnam(sys_get_temp_dir(), 'test');
|
||||
file_put_contents($tempSource, 'test content');
|
||||
file_put_contents($tempSource, $content);
|
||||
$encryptionService->encryptFile($tempSource, $encryptedPath, $key);
|
||||
unlink($tempSource);
|
||||
$file->update(['file_size' => strlen($content)]);
|
||||
}
|
||||
|
||||
$this->withSession(['share_password_'.$share->token => null])
|
||||
$response = $this->withSession(['share_password_'.$share->token => null])
|
||||
->get(route('share.download.all', $share));
|
||||
|
||||
$response->assertDownload();
|
||||
|
||||
expect($share->fresh()->download_count)->toBe(1);
|
||||
});
|
||||
|
||||
test('zip download produces a valid archive', function () {
|
||||
Storage::fake('shares');
|
||||
|
||||
$share = createShareWithFile();
|
||||
$share->load('files');
|
||||
|
||||
$encryptionService = app(FileEncryptionService::class);
|
||||
$key = $share->encryption_key;
|
||||
$content = 'hello zip content';
|
||||
|
||||
foreach ($share->files as $file) {
|
||||
$dir = Storage::disk('shares')->path($share->token);
|
||||
if (! is_dir($dir)) {
|
||||
mkdir($dir, 0755, true);
|
||||
}
|
||||
$encryptedPath = $dir.'/'.basename($file->stored_path);
|
||||
$tempSource = tempnam(sys_get_temp_dir(), 'test');
|
||||
file_put_contents($tempSource, $content);
|
||||
$encryptionService->encryptFile($tempSource, $encryptedPath, $key);
|
||||
unlink($tempSource);
|
||||
$file->update(['file_size' => strlen($content)]);
|
||||
}
|
||||
|
||||
$response = $this->get(route('share.download.all', $share));
|
||||
$response->assertDownload();
|
||||
|
||||
$zipPath = $response->getFile()->getPathname();
|
||||
|
||||
$zip = new \ZipArchive;
|
||||
$result = $zip->open($zipPath);
|
||||
|
||||
expect($result)->toBe(true);
|
||||
expect($zip->numFiles)->toBe(1);
|
||||
expect($zip->statIndex(0)['size'])->toBe(strlen($content));
|
||||
|
||||
$zip->close();
|
||||
});
|
||||
|
||||
test('last download streams successfully before auto-delete', function () {
|
||||
Storage::fake('shares');
|
||||
|
||||
$share = createShareWithFile();
|
||||
$share->update(['max_downloads' => 1]);
|
||||
$share->load('files');
|
||||
|
||||
$encryptionService = app(FileEncryptionService::class);
|
||||
$key = $share->encryption_key;
|
||||
$content = 'last download content';
|
||||
|
||||
foreach ($share->files as $file) {
|
||||
$dir = Storage::disk('shares')->path($share->token);
|
||||
if (! is_dir($dir)) {
|
||||
mkdir($dir, 0755, true);
|
||||
}
|
||||
$encryptedPath = $dir.'/'.basename($file->stored_path);
|
||||
$tempSource = tempnam(sys_get_temp_dir(), 'test');
|
||||
file_put_contents($tempSource, $content);
|
||||
$encryptionService->encryptFile($tempSource, $encryptedPath, $key);
|
||||
unlink($tempSource);
|
||||
$file->update(['file_size' => strlen($content)]);
|
||||
}
|
||||
|
||||
$response = $this->get(route('share.download.all', $share));
|
||||
$response->assertDownload();
|
||||
|
||||
// Share was deleted after download
|
||||
expect(Share::query()->find($share->id))->toBeNull();
|
||||
});
|
||||
|
||||
test('share auto-deletes after reaching download limit', function () {
|
||||
Storage::fake('shares');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user