You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Laravel中读取AWS及本地Zip文件夹并在线展示内容?

在Laravel中实现AWS Zip文件在线预览方案

一、AWS S3中Zip文件的列表展示与在线预览实现

1. 获取S3上的Zip文件列表

先在Laravel后端写接口拉取AWS S3中的Zip文件信息,前端用jQuery请求并渲染成可点击的列表:

后端路由

Route::get('/zip-files', 'ZipFileController@list')->name('zip.files');

控制器方法

use Illuminate\Support\Facades\Storage;

class ZipFileController extends Controller
{
    public function list()
    {
        $disk = Storage::disk('s3');
        // 筛选出所有.zip后缀的文件,可根据实际存储路径调整过滤规则
        $zipFiles = collect($disk->files())->filter(function ($file) {
            return str_ends_with($file, '.zip');
        })->map(function ($file) use ($disk) {
            return [
                'name' => basename($file),
                'key' => $file,
                'size' => $disk->size($file)
            ];
        });

        return response()->json($zipFiles);
    }
}

前端jQuery渲染代码

<div id="zipList"></div>

<script>
$(document).ready(function() {
    $.get('/zip-files', function(data) {
        const listContainer = $('#zipList');
        data.forEach(function(zip) {
            const link = $('<a>')
                .attr('href', `/zip-preview/${zip.key}`)
                .attr('target', '_blank')
                .text(`${zip.name} (${formatSize(zip.size)})`);
            listContainer.append($('<div>').append(link).css('margin', '5px 0'));
        });
    });

    // 文件大小格式化辅助函数
    function formatSize(bytes) {
        if (bytes < 1024) return `${bytes} B`;
        else if (bytes < 1048576) return `${(bytes / 1024).toFixed(2)} KB`;
        else return `${(bytes / 1048576).toFixed(2)} MB`;
    }
});
</script>

2. 创建Zip内容预览页面

用户点击Zip链接后,后端解析Zip内的目录结构,返回给前端展示;点击Zip内的文件时,再请求后端读取对应文件内容并展示。

后端路由

Route::get('/zip-preview/{fileKey}', 'ZipPreviewController@show')->name('zip.preview');
Route::get('/zip-file-content/{fileKey}/{fileName}', 'ZipPreviewController@showFileContent')->name('zip.file.content');

控制器方法

use Illuminate\Support\Facades\Storage;
use ZipArchive;

class ZipPreviewController extends Controller
{
    public function show($fileKey)
    {
        $disk = Storage::disk('s3');
        if (!$disk->exists($fileKey)) abort(404, 'Zip文件不存在');

        // 将S3文件流写入临时文件,避免直接下载大文件
        $tempPath = tempnam(sys_get_temp_dir(), 'aws_zip');
        $fileStream = $disk->readStream($fileKey);
        file_put_contents($tempPath, stream_get_contents($fileStream));
        fclose($fileStream);

        $zip = new ZipArchive();
        if ($zip->open($tempPath) !== true) {
            unlink($tempPath);
            abort(400, '无法解析Zip文件');
        }

        // 遍历Zip内的所有文件/目录
        $contents = [];
        for ($i = 0; $i < $zip->numFiles; $i++) {
            $stat = $zip->statIndex($i);
            $contents[] = [
                'name' => $stat['name'],
                'size' => $stat['size'],
                'is_dir' => substr($stat['name'], -1) === '/'
            ];
        }

        $zip->close();
        unlink($tempPath); // 及时清理临时文件

        return view('zip-preview', [
            'contents' => $contents,
            'fileKey' => $fileKey,
            'zipName' => basename($fileKey)
        ]);
    }

    public function showFileContent($fileKey, $fileName)
    {
        $disk = Storage::disk('s3');
        if (!$disk->exists($fileKey)) abort(404, 'Zip文件不存在');

        $tempPath = tempnam(sys_get_temp_dir(), 'aws_zip');
        $fileStream = $disk->readStream($fileKey);
        file_put_contents($tempPath, stream_get_contents($fileStream));
        fclose($fileStream);

        $zip = new ZipArchive();
        if ($zip->open($tempPath) !== true) {
            unlink($tempPath);
            abort(400, '无法解析Zip文件');
        }

        // 获取指定文件的内容
        $content = $zip->getFromName($fileName);
        if ($content === false) {
            $zip->close();
            unlink($tempPath);
            abort(404, '该文件不存在于Zip中');
        }

        $zip->close();
        unlink($tempPath);

        // 根据文件类型返回对应响应
        $mimeType = mime_content_type($fileName);
        return response($content)->header('Content-Type', $mimeType);
    }
}

预览页面视图(zip-preview.blade.php)

<h1>预览: {{ $zipName }}</h1>
<div id="zipContents" style="margin-bottom: 20px;">
    @foreach($contents as $item)
        <div class="zip-item" style="margin: 5px 0;">
            @if($item['is_dir'])
                <span>📁</span> {{ $item['name'] }}
            @else
                <span>📄</span> 
                <a href="javascript:void(0)" class="file-link" data-file="{{ $item['name'] }}">
                    {{ $item['name'] }} ({{ formatSize($item['size']) }})
                </a>
            @endif
        </div>
    @endforeach
</div>
<div id="fileContent" style="padding: 10px; border: 1px solid #ccc; display: none;"></div>

<script>
$(document).ready(function() {
    $('.file-link').click(function() {
        const fileName = $(this).data('file');
        const fileKey = '{{ $fileKey }}';
        
        $.get(`/zip-file-content/${fileKey}/${encodeURIComponent(fileName)}`, function(data) {
            const contentDiv = $('#fileContent');
            contentDiv.show();
            
            // 根据MIME类型展示内容
            const mimeType = this.getResponseHeader('Content-Type');
            if (mimeType.startsWith('text/')) {
                contentDiv.html(`<pre>${data}</pre>`);
            } else if (mimeType.startsWith('image/')) {
                const blob = new Blob([data], { type: mimeType });
                const url = URL.createObjectURL(blob);
                contentDiv.html(`<img src="${url}" style="max-width: 100%;">`);
            } else {
                contentDiv.text('暂不支持该文件类型预览');
            }
        }).fail(function() {
            alert('无法读取文件内容');
        });
    });
});

function formatSize(bytes) {
    if (bytes < 1024) return `${bytes} B`;
    else if (bytes < 1048576) return `${(bytes / 1024).toFixed(2)} KB`;
    else return `${(bytes / 1048576).toFixed(2)} MB`;
}
</script>

核心注意点

  • 临时文件必须及时删除,避免服务器磁盘被占用
  • 超大Zip文件建议用ZipArchive::getStream流式读取单个文件,避免内存溢出
  • 可根据业务需求添加权限验证,限制未授权用户访问

二、在线打开本地Zip文件夹的方案

浏览器受安全策略限制,无法直接读取本地文件系统的Zip,有两种可行方案:

1. 前端JS直接解析(无需上传)

使用jszip库在浏览器端读取用户上传的Zip文件,直接解析展示:

<input type="file" id="localZipInput" accept=".zip">
<div id="localZipContent" style="margin-top: 20px;"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
<script>
$('#localZipInput').change(function(e) {
    const file = e.target.files[0];
    if (!file) return;

    JSZip.loadAsync(file).then(function(zip) {
        const contentDiv = $('#localZipContent');
        contentDiv.empty();
        
        zip.forEach(function(relativePath, zipEntry) {
            const item = $('<div>').css('margin', '5px 0');
            if (zipEntry.dir) {
                item.html(`<span>📁</span> ${relativePath}`);
            } else {
                item.html(`<span>📄</span> ${relativePath}`);
                item.click(function() {
                    // 根据文件类型选择读取方式
                    if (relativePath.match(/\.(txt|md|json)$/i)) {
                        zipEntry.async('text').then(function(text) {
                            $('<pre>').text(text).appendTo(contentDiv);
                        });
                    } else if (relativePath.match(/\.(jpg|jpeg|png|gif)$/i)) {
                        zipEntry.async('blob').then(function(blob) {
                            const url = URL.createObjectURL(blob);
                            $('<img>').attr('src', url).css('max-width', '100%').appendTo(contentDiv);
                        });
                    } else {
                        alert('暂不支持该文件类型预览');
                    }
                });
            }
            contentDiv.append(item);
        });
    }).catch(function(err) {
        alert(`无法打开Zip文件:${err.message}`);
    });
});
</script>

2. 上传到服务器后预览

让用户将本地Zip上传到AWS S3或服务器,之后复用第一部分的Laravel预览逻辑即可。


内容的提问来源于stack exchange,提问作者Eka Hanif

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 23:33:10