全方位站长技能、SEO优化学习平台
免费香港宝塔虚拟主机 免费香港Kangle虚拟主机
当前位置:网站首页 > 站长笔记 > 正文

原创PHP代码:一款轻量高效的服务器端PHP文件打包工具

作者:文煞发布时间:2025-10-24分类:站长笔记浏览:762


温馨提示:手机扫码可阅读当前文章!
文章简介:在日常开发或服务器管理中,我们经常会遇到这样的场景:需要从服务器上下载多个分散的文件(如PHP脚本、HTML页面、配置文件),却不想通过复杂的FTP工具逐个传输;想要快速打包某一目录下的代码文件分享给同事,却发现服务器上没有预装压缩工具;需...

在日常开发或服务器管理中,我们经常会遇到这样的场景:需要从服务器上下载多个分散的文件(如PHP脚本、HTML页面、配置文件),却不想通过复杂的FTP工具逐个传输;想要快速打包某一目录下的代码文件分享给同事,却发现服务器上没有预装压缩工具;需要核对服务器上的文件结构和内容,避免下载后才发现漏选或错选文件……这时,一款能在服务器端直接运行、可视化操作的文件打包工具就成了刚需。

今天要介绍的这款PHP单文件文件打包工具,是本人原创设计的,正是为解决这些痛点而生。它无需复杂部署,只需将单个PHP文件上传到服务器目录,通过浏览器访问即可实现目录扫描、文件选择、多格式打包、内容预览等功能,尤其适合开发者、运维人员在无本地压缩工具或FTP权限受限的场景下使用。下面,我们将从工具核心功能、使用方法、优势亮点、注意事项等方面,进行全面且通俗的讲解。

原创PHP代码:一款轻量高效的服务器端PHP文件打包工具  第1张

一、工具核心功能:贴合实际需求,覆盖打包全流程

这款工具的所有功能都基于PHP开发,完全在服务器端运行,无需依赖本地软件。从代码逻辑来看,它的核心能力可分为“文件扫描与管理”“多格式打包”“内容预览与下载”“状态监控与统计”四大模块,每个模块都精准对应实际使用中的需求。

1. 智能目录扫描:自动识别文件,跳过冗余内容

工具的第一步是“找到文件”,这依赖于代码中的 scanDirectoryForStructure() 函数。当你访问工具页面时,它会递归扫描当前目录及所有子目录,自动识别文件和文件夹,同时规避不必要的冗余内容——这是它的一大贴心设计。

  • 自动跳过关键文件:代码中预设了 $skipItems 数组,会自动跳过工具自身相关的文件(如生成的打包文件 files.txt 、工具本体 file.php 、服务器配置文件 .user.ini ),避免打包时把工具文件也包含进去,减少冗余。
  • 兼容不同服务器环境:扫描过程中会先判断目录是否“存在且可读”(通过 is_dir() 和 is_readable() 函数),如果某目录因权限问题无法访问,会自动跳过并继续扫描其他目录,不会导致工具崩溃。
  • 清晰区分文件类型:扫描后会给每个条目标记“file”(文件)或“folder”(文件夹)类型,同时记录文件的大小(通过 filesize() 函数),后续在界面上会用不同图标区分(如文件夹用📁,PHP文件用🐘,图片用🖼️),直观易懂。

举个例子:如果你的服务器目录下有“src”“config”“public”三个子目录,以及“index.php”“README.md”两个文件,工具会自动把这些内容全部扫描出来,并用层级结构展示,就像在本地文件管理器里一样清晰。

原创PHP代码:一款轻量高效的服务器端PHP文件打包工具  第2张

2. 灵活的文件选择:批量操作,减少重复劳动

找到文件后,下一步就是“选对文件”。工具提供了多种文件选择方式,彻底告别“逐个勾选”的繁琐,这部分功能由前端JS逻辑与后端PHP判断协同实现。

  • 全选/取消全选:界面顶部的“全选”按钮会自动勾选当前扫描到的所有文件(包括子目录下的文件),“取消全选”则一键清空选择,适合需要打包大部分或全部文件的场景。
  • 文件夹级选择:勾选文件夹前的复选框时,工具会自动选中该文件夹下的所有子文件和子目录(通过 handleFolderSelection() 函数实现),比如勾选“src”文件夹,就会自动选中“src”下的 api.php “utils”子目录及其中的 helper.php ,无需逐个展开勾选。
  • 折叠/展开目录:文件夹前的“▶”“▼”按钮可以折叠或展开子目录,当目录层级较多(如嵌套5-6层)时,折叠无关目录能让界面更简洁,避免视觉混乱。
  • 精准筛选文件:如果只需打包某几个零散文件(如“config/db.php”“public/style.css”),可以直接展开对应目录,单独勾选目标文件,灵活度拉满。

比如你需要打包“config”目录下的所有配置文件和“public”目录下的HTML、CSS文件,只需勾选“config”文件夹和“public”文件夹,工具会自动处理其中的所有子文件,无需手动逐个选择。

3. 多格式打包:适配不同场景,兼容多数服务器

打包功能是工具的核心,代码中的 getSupportedFormats() 函数会先检测服务器环境,自动启用支持的压缩格式,避免出现“点击打包却提示不支持”的尴尬。目前工具支持的格式分为“基础格式”和“扩展格式”两类,覆盖绝大多数使用场景。

(1)默认支持:TXT文本格式(带内容预览)

TXT格式是工具的“保底选项”,无论服务器环境如何,都会支持。它的特殊之处在于不仅能打包文件列表,还能包含文件的具体内容,对应代码中的 generateTxtFile() 函数。

生成的TXT文件会分为三个部分:

  • 文件结构:列出所有选中文件的路径和总数量,比如“总文件数: 3”“- config/db.php”“- public/index.html”,让你快速了解打包范围;
  • 文件内容:按路径顺序展示每个文件的具体内容,比如“config/db.php”的数据库配置代码、“public/index.html”的HTML结构,中间用“==...==”分隔,清晰区分不同文件;
  • 处理统计:最后会显示“总计处理文件: 3 个”,确认是否所有选中文件都已成功处理。

更实用的是,TXT格式支持浏览器预览——生成后不会直接下载,而是在页面上显示预览框,你可以先核对文件内容是否正确(比如确认配置文件没有敏感信息),再点击“下载文件”保存到本地,避免下载后才发现漏选或内容错误。

(2)扩展支持:多种压缩格式(直接下载)

除了TXT,工具还会根据服务器环境自动启用压缩格式,常见的包括:

  • ZIP格式:最通用的压缩格式,几乎所有操作系统(Windows、macOS、Linux)都能直接解压。代码中通过 ZipArchive 类实现,只要服务器PHP环境开启了该扩展(大部分主流服务器都会开启),就能使用;
  • GZ格式(tar.gz):在Linux/macOS环境下常用的压缩格式,代码中会先将文件打包成tar格式,再通过 gzencode() 函数压缩为tar.gz,适合需要在服务器端进一步处理压缩包的场景;
  • RAR格式:需服务器安装RAR扩展( RarArchive 类),压缩率略高于ZIP,适合习惯使用WinRAR的用户;
  • 7Z格式:压缩率最高的格式之一,但需要服务器上预装7z二进制文件(通过 exec() 函数检测“which 7z”),适合打包大型文件(如多个图片、视频)以节省存储空间。

这些压缩格式的打包逻辑各有优化,比如ZIP格式会保留文件的相对路径(打包后解压仍能保持“config/db.php”的层级),GZ格式会严格遵循tar标准以确保兼容性,避免出现“解压后文件混乱”的问题。

4. 可视化状态监控:进度清晰,统计直观

很多打包工具在生成文件时会让用户“盲目等待”,而这款工具通过“进度条”和“统计面板”,让整个过程可视化,对应代码中的 showStats() 和进度条模拟逻辑。

  • 实时进度条:点击“生成文件”后,页面会显示一个从0%到100%的进度条,虽然是模拟进度(实际生成速度取决于文件大小和数量),但能有效缓解“不知道还要等多久”的焦虑,尤其在打包大量文件时更实用;
  • 核心统计面板:界面下方的统计区域会实时显示4个关键数据:
  1. 总文件数:当前目录下所有可识别的文件总数(不含文件夹);
  2. 总文件夹数:当前目录下所有子目录的总数;
  3. 已选择文件:你当前勾选的文件数量,避免漏选或多选;
  4. 支持格式:服务器当前支持的打包格式数量(如显示“3”,代表支持TXT、ZIP、GZ)。

比如你扫描到服务器上有12个文件、3个文件夹,勾选了8个文件,支持4种格式,这些数据会实时显示在统计面板上,让你对当前操作范围和工具能力一目了然。

5. 人性化下载体验:自定义文件名,适配不同需求

工具在下载环节也做了细节优化,避免“每次下载都是默认名”的麻烦:

  • 自定义输出文件名:在“打包选项”中,你可以输入自定义文件名(如“20240510_网站代码备份”),工具会自动添加对应格式的后缀(如ZIP格式会变成“20240510_网站代码备份.zip”,TXT格式会变成“20240510_网站代码备份.txt”);
  • 区分下载逻辑:TXT格式会先预览再提供下载,压缩格式(ZIP、GZ等)则在生成后直接触发浏览器下载,无需额外点击;
  • 兼容性下载:代码中通过 Blob 对象和 URL.createObjectURL() 实现前端下载,支持Chrome、Firefox、Edge、Safari等主流浏览器,不会出现“下载按钮无效”的问题。
  • 原创PHP代码:一款轻量高效的服务器端PHP文件打包工具  第3张

二、工具使用教程:3步上手,无需技术背景

这款工具的最大优势之一就是“零门槛使用”,无论你是资深开发者还是刚接触服务器的新手,只需3步就能完成从部署到打包的全过程。

第一步:部署工具到服务器

  1. 准备工具文件:将工具的PHP文件(假设文件名为 filepacker.php ,即代码中的 file.php )保存到本地;
  2. 上传文件:通过FTP工具(如FileZilla)或服务器面板(如宝塔面板),将 filepacker.php 上传到你需要打包文件的目标目录(比如你想打包“/www/wwwroot/mywebsite”下的文件,就把 filepacker.php 上传到这个目录);
  3. 确认权限:确保 filepacker.php 所在目录有“读取”权限(服务器上通常默认开启),否则工具无法扫描目录下的文件。

第二步:扫描并选择文件

  1. 访问工具:打开浏览器,在地址栏输入“服务器域名/目录路径/filepacker.php”(比如“http://www.mywebsite.com/filepacker.php”),加载完成后工具会自动开始扫描目录;
  2. 查看文件列表:扫描完成后,页面会以层级结构显示所有文件和文件夹,文件夹前有“▶”按钮,点击可展开子目录,文件旁会显示大小(如“index.php 2.1 KB”);
  3. 选择目标文件:
  • 如需打包全部文件:点击顶部“全选”按钮;
  • 如需打包部分目录:勾选目标文件夹(如“src”“config”),工具会自动选中子文件;
  • 如需打包零散文件:展开对应目录,单独勾选目标文件(如“public/style.css”);
  • 选错了?点击“取消全选”重新选择,或直接取消单个文件的勾选。

第三步:设置打包选项并下载

  1. 选择输出格式:在“打包选项”的“输出格式”中,选择你需要的格式:
  • 想预览内容或只需简单打包:选“TXT (文本预览)”;
  • 想压缩节省空间或分享给他人:选“ZIP”(兼容性最好);
  • 服务器环境支持且需要高压缩率:选“7Z”或“GZ”;
  1. 自定义文件名:在“输出文件名”输入框中,填写你想要的文件名(如“mywebsite_backup_202405”),无需手动加后缀;
  2. 生成文件:点击“生成文件”按钮,此时会显示进度条,等待进度条完成;
  3. 下载文件:
  • 若选择TXT格式:进度条完成后会显示“文件内容预览”,核对内容无误后,点击“下载文件”保存;
  • 若选择压缩格式(ZIP/GZ等):进度条完成后会自动触发下载,文件会保存到浏览器默认的下载目录。

三、工具优势亮点:为什么选择这款工具?

在众多文件打包方案(如本地压缩后上传、服务器命令行压缩、FTP逐个下载)中,这款PHP工具的优势非常明显,尤其适合特定场景下的需求:

1. 单文件部署:无需安装,即传即用

工具仅包含一个PHP文件,无需解压、配置环境变量或安装依赖(除部分压缩格式需服务器预装扩展外),上传到目标目录后直接通过浏览器访问即可使用。相比需要部署后端服务的工具(如基于Node.js的打包工具),它的部署成本几乎为零,哪怕是新手也能在1分钟内完成部署。

2. 服务器端运行:突破本地环境限制

所有操作都在服务器端完成,无需依赖本地电脑的软件环境:

  • 无需安装压缩软件(如WinRAR、7-Zip),哪怕你在公共电脑上操作,只要能访问浏览器就能打包;
  • 避免FTP传输的繁琐:如果需要打包100个小文件,用FTP逐个下载需要反复点击,而用工具打包后只需下载一个压缩包,节省大量时间;
  • 适配弱网环境:压缩后的文件体积更小,在网络速度较慢时,下载一个10MB的ZIP包比下载100个100KB的零散文件快得多。

3. 可视化操作:比命令行更易用

对于不熟悉服务器命令行(如Linux的 zip / tar 命令)的用户,可视化界面的优势不言而喻:

  • 无需记忆复杂命令(如 zip -r backup.zip src/ config/ ),用鼠标点击就能完成选择和打包;
  • 实时反馈状态:文件是否选中、格式是否支持、生成进度如何,都能在界面上直观看到,避免命令行“黑盒操作”导致的错误(如输错目录路径导致打包失败)。

4. 安全跳过关键文件:避免泄露敏感信息

代码中预设了跳过工具自身文件和服务器配置文件(如 .user.ini )的逻辑,这些文件通常包含工具运行信息或服务器配置,打包后如果分享给他人,可能存在安全风险。工具的自动跳过功能,能减少“误打包敏感文件”的概率,提升使用安全性。

5. 兼容性强:适配多数PHP环境

工具基于PHP 5.6+开发(兼容PHP 7.x、8.x),支持绝大多数主流服务器环境(如Apache、Nginx、IIS),只要服务器开启了基础的PHP运行环境(这是网站服务器的标配),就能正常使用。对于压缩格式的依赖(如 ZipArchive 类),大部分云服务器(如阿里云、腾讯云)的默认PHP配置都会开启,无需额外手动配置。

四、注意事项与常见问题

虽然工具易用且稳定,但在使用过程中,仍有一些细节需要注意,以避免出现问题;同时,我们也整理了一些常见问题的解决方案:

注意事项

1. 权限问题:确保目录可读取

工具扫描文件时需要目录有“读取权限”(服务器上通常用 chmod 755 设置目录权限, chmod 644 设置文件权限)。如果某目录显示“没有找到文件或目录不可读”,可能是该目录权限不足,需要通过服务器面板或FTP工具调整权限。

2. 敏感文件:谨慎选择打包范围

服务器目录中可能包含数据库密码(如 config/db.php )、API密钥等敏感信息,打包前务必确认选中的文件中没有这些内容,避免将压缩包分享给他人时泄露信息。如果需要分享代码,建议先删除敏感信息或替换为占位符。

3. 文件大小:避免打包超大文件

虽然工具支持打包大文件,但生成压缩包时会占用服务器内存和磁盘空间。如果需要打包单个超过1GB的文件(如视频、大型数据库备份),建议直接通过FTP下载,避免因服务器内存不足导致打包失败。

4. 安全访问:限制工具访问权限

工具能扫描和打包目录下的文件,存在一定的安全风险(如被未授权用户访问,导致文件泄露)。使用完成后,建议及时删除服务器上的 filepacker.php 文件;如果需要长期使用,可通过服务器面板设置访问密码(如Nginx的 auth_basic 认证),仅允许指定用户访问。

常见问题(FAQ)

Q1:为什么界面上没有显示RAR/7Z格式的选项?

A:这是因为服务器环境不支持对应的格式:

  • 没有RAR选项:服务器PHP未安装 rar 扩展,需联系服务器管理员开启;
  • 没有7Z选项:服务器未预装7z二进制文件(Linux需通过 yum install p7zip 或 apt install p7zip-full 安装,Windows需手动安装并配置环境变量)。 如果无法修改服务器配置,建议选择ZIP格式(兼容性最好)。

Q2:扫描文件时提示“没有找到文件或目录不可读”,怎么办?

A:首先检查工具所在目录的权限,确保目录有“读取”权限;其次,确认该目录下确实有文件(如果是刚创建的空目录,扫描结果也会为空);最后,如果目录下有符号链接(软链接),工具可能无法识别,建议直接访问实际目录。

Q3:生成TXT文件后,预览内容显示乱码,怎么解决?

A:这是因为文件编码与浏览器默认编码不一致(如文件是GBK编码,而浏览器用UTF-8解码)。解决方案:生成TXT文件后,用记事本打开,点击“文件”→“另存为”,在“编码”选项中选择“UTF-8”,保存后即可正常显示。

Q4:生成ZIP文件后,解压时提示“压缩包损坏”,是什么原因?

A:可能有两种原因:

  1. 生成过程中网络中断:导致下载的压缩包不完整,重新生成并下载即可;
  2. 服务器内存不足:打包大量文件时,服务器内存不够导致压缩过程中断,建议分多次打包(如先打包“src”目录,再打包“config”目录),或删除部分不必要的文件后再打包。

Q5:可以自定义跳过的文件吗?比如我想跳过“log”目录下的日志文件。

A:目前工具的跳过列表( 数组)是在代码中预设的,无法通过界面自定义。如果需要跳过特定文件或目录,可手动修改代码:在skipItems 数组中添加需要跳过的路径(如 'log/' 代表跳过“log”目录, 'tmp/cache.php' 代表跳过“tmp”目录下的 cache.php ),修改后保存并重新上传到服务器即可。

五、总结

这款PHP文件打包工具虽然轻量,但功能却十分精准地覆盖了服务器端文件打包的核心需求:从智能扫描目录、灵活选择文件,到多格式打包、可视化预览,再到人性化的下载体验,每一个功能都围绕“简单、高效、易用”的目标设计。

无论是开发者需要备份服务器上的代码文件,运维人员需要打包日志文件,还是普通用户需要分享服务器上的零散文件,这款工具都能成为得力助手。它无需复杂部署,突破本地环境限制,让文件打包从“繁琐操作”变成“一键完成”,尤其适合在无本地工具、弱网环境或命令行不熟练的场景下使用。

如果你经常需要在服务器上处理文件打包需求,不妨试试这款工具——只需一个PHP文件,就能解决你的所有烦恼。代码如下:

<?php
// 设置输出文件名
$outputFile = 'files.txt';

// 要跳过的文件和文件夹列表
$skipItems = [
    $outputFile,
    'file.php',
    '.user.ini',
];

// 检查服务器支持的压缩格式
function getSupportedFormats() {
    $formats = ['txt']; // TXT总是支持的
    
    // 检查Zip支持
    if (class_exists('ZipArchive')) {
        $formats[] = 'zip';
    }
    
    // 检查Gz支持
    if (function_exists('gzencode')) {
        $formats[] = 'gz';
    }
    
    // 检查Rar支持(需要rar扩展)
    if (class_exists('RarArchive')) {
        $formats[] = 'rar';
    }
    
    // 检查7z支持(需要7zip二进制文件)
    if (function_exists('exec')) {
        $output = [];
        $returnCode = 0;
        @exec('which 7z 2>/dev/null', $output, $returnCode);
        if ($returnCode === 0 && !empty($output)) {
            $formats[] = '7z';
        }
    }
    
    return $formats;
}

// 递归遍历目录并收集文件结构
function scanDirectoryForStructure($dir, $baseDir = '', $skipItems = []) {
    $structure = [];
    
    // 检查目录是否存在且可读
    if (!is_dir($dir) || !is_readable($dir)) {
        return $structure;
    }
    
    $files = @scandir($dir);
    if ($files === false) {
        return $structure;
    }
    
    foreach ($files as $file) {
        if ($file == '.' || $file == '..') {
            continue;
        }
        
        $filePath = $dir . DIRECTORY_SEPARATOR . $file;
        $relativePath = ($baseDir ? $baseDir . DIRECTORY_SEPARATOR : '') . $file;
        
        // 检查是否需要跳过该文件或文件夹
        $shouldSkip = false;
        foreach ($skipItems as $skipItem) {
            if (basename($filePath) === $skipItem) {
                $shouldSkip = true;
                break;
            }
        }
        
        if ($shouldSkip) {
            continue;
        }
        
        if (is_dir($filePath)) {
            $item = [
                'name' => $file,
                'path' => $relativePath,
                'type' => 'folder',
                'children' => scanDirectoryForStructure($filePath, $relativePath, $skipItems)
            ];
            $structure[] = $item;
        } else if (is_file($filePath) && is_readable($filePath)) {
            $item = [
                'name' => $file,
                'path' => $relativePath,
                'type' => 'file',
                'size' => filesize($filePath)
            ];
            $structure[] = $item;
        }
    }
    
    return $structure;
}

// 根据选择的文件生成TXT文件
function generateTxtFile($selectedFiles) {
    global $skipItems;
    
    $outputContent = "=== 文件结构 ===\n";
    $outputContent .= "总文件数: " . count($selectedFiles) . "\n\n";
    
    foreach ($selectedFiles as $file) {
        $outputContent .= "- " . $file . "\n";
    }
    
    $outputContent .= "\n=== 文件内容 ===\n\n";
    
    $processedCount = 0;
    foreach ($selectedFiles as $file) {
        if (is_file($file) && is_readable($file)) {
            $outputContent .= "==...==\n";
            $outputContent .= $file . "\n";
            $content = @file_get_contents($file);
            if ($content !== false) {
                $outputContent .= $content . "\n\n";
                $processedCount++;
            }
        }
    }
    
    $outputContent .= "=== 文件内容结束 ===\n";
    $outputContent .= "总计处理文件: " . $processedCount . " 个\n";
    
    return $outputContent;
}

// 修复的ZIP文件生成函数
function generateZipFile($selectedFiles) {
    $zip = new ZipArchive();
    
    // 创建内存中的ZIP文件
    $tempFile = tempnam(sys_get_temp_dir(), 'zip');
    
    if ($zip->open($tempFile, ZipArchive::CREATE | ZipArchive::OVERWRITE) === TRUE) {
        foreach ($selectedFiles as $file) {
            if (is_file($file) && is_readable($file)) {
                // 使用相对路径作为ZIP内的路径
                $zip->addFile($file, $file);
            }
        }
        
        if (!$zip->close()) {
            return false;
        }
        
        // 读取文件内容
        $content = file_get_contents($tempFile);
        unlink($tempFile);
        
        return $content;
    }
    
    if (file_exists($tempFile)) {
        unlink($tempFile);
    }
    
    return false;
}

// 修复的GZ文件生成函数 - 使用更标准的tar格式
function generateGzFile($selectedFiles) {
    $tarContent = '';
    
    foreach ($selectedFiles as $file) {
        if (is_file($file) && is_readable($file)) {
            $content = file_get_contents($file);
            if ($content !== false) {
                $fileInfo = stat($file);
                $mode = 0644;
                $uid = 0;
                $gid = 0;
                $size = strlen($content);
                $mtime = time();
                $typeflag = '0'; // 普通文件
                $linkname = '';
                $magic = "ustar";
                $version = "00";
                $uname = "root";
                $gname = "root";
                $devmajor = 0;
                $devminor = 0;
                $prefix = '';
                
                // 文件名处理(不超过100字符)
                $name = $file;
                if (strlen($name) > 100) {
                    $prefix = substr($name, 0, 155);
                    $name = substr($name, -100);
                }
                
                // 创建tar头部
                $header = pack("a100a8a8a8a12a12a8a1a100a6a2a32a32a8a8a155a12",
                    $name, // 文件名
                    sprintf("%07o", $mode), // 文件模式
                    sprintf("%07o", $uid), // 用户ID
                    sprintf("%07o", $gid), // 组ID
                    sprintf("%011o", $size), // 文件大小
                    sprintf("%011o", $mtime), // 修改时间
                    "        ", // 校验和占位符
                    $typeflag, // 文件类型
                    $linkname, // 链接名
                    $magic, // magic
                    $version, // 版本
                    $uname, // 用户名
                    $gname, // 组名
                    sprintf("%07o", $devmajor), // 主设备号
                    sprintf("%07o", $devminor), // 次设备号
                    $prefix, // 前缀
                    "" // 填充
                );
                
                // 计算校验和
                $checksum = 0;
                for ($i = 0; $i < 512; $i++) {
                    $checksum += ord($header[$i]);
                }
                
                // 写入校验和
                $header = substr_replace($header, sprintf("%07o", $checksum) . "\0", 148, 8);
                
                $tarContent .= $header;
                $tarContent .= $content;
                
                // 填充到512字节边界
                $padding = 512 - ($size % 512);
                if ($padding < 512) {
                    $tarContent .= str_repeat("\0", $padding);
                }
            }
        }
    }
    
    // 添加结束标记(两个512字节的零块)
    $tarContent .= str_repeat("\0", 1024);
    
    // 压缩为gz
    return gzencode($tarContent, 9);
}

// 处理AJAX请求
function handleAjaxRequest() {
    global $skipItems;
    
    if (isset($_GET['action']) && $_GET['action'] === 'scan') {
        // 返回文件结构
        $fileStructure = scanDirectoryForStructure('.', '', $GLOBALS['skipItems']);
        $supportedFormats = getSupportedFormats();
        
        header('Content-Type: application/json; charset=utf-8');
        echo json_encode([
            'success' => true,
            'fileStructure' => $fileStructure,
            'supportedFormats' => $supportedFormats
        ]);
        return true;
    }
    
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $input = json_decode(file_get_contents('php://input'), true);
        
        if (isset($input['action']) && $input['action'] === 'generate') {
            $selectedFiles = $input['files'] ?? [];
            $format = $input['format'] ?? 'txt';
            $filename = $input['filename'] ?? '打包文件';
            
            if (empty($selectedFiles)) {
                header('Content-Type: application/json');
                echo json_encode(['success' => false, 'message' => '没有选择文件']);
                return true;
            }
            
            $content = '';
            $contentType = '';
            $downloadFilename = '';
            
            switch ($format) {
                case 'txt':
                    $content = generateTxtFile($selectedFiles);
                    $contentType = 'text/plain; charset=utf-8';
                    $downloadFilename = $filename . '.txt';
                    break;
                    
                case 'zip':
                    if (class_exists('ZipArchive')) {
                        $content = generateZipFile($selectedFiles);
                        if ($content === false) {
                            header('Content-Type: application/json');
                            echo json_encode(['success' => false, 'message' => '生成ZIP文件失败']);
                            return true;
                        }
                        $contentType = 'application/zip';
                        $downloadFilename = $filename . '.zip';
                    } else {
                        header('Content-Type: application/json');
                        echo json_encode(['success' => false, 'message' => '服务器不支持ZIP格式']);
                        return true;
                    }
                    break;
                    
                case 'gz':
                    if (function_exists('gzencode')) {
                        $content = generateGzFile($selectedFiles);
                        $contentType = 'application/gzip';
                        $downloadFilename = $filename . '.tar.gz';
                    } else {
                        header('Content-Type: application/json');
                        echo json_encode(['success' => false, 'message' => '服务器不支持GZ格式']);
                        return true;
                    }
                    break;
                    
                default:
                    header('Content-Type: application/json');
                    echo json_encode(['success' => false, 'message' => '不支持的格式: ' . $format]);
                    return true;
            }
            
            if ($content === false) {
                header('Content-Type: application/json');
                echo json_encode(['success' => false, 'message' => '生成文件失败']);
                return true;
            }
            
            // 对于TXT格式,返回JSON包含内容用于预览
            if ($format === 'txt') {
                header('Content-Type: application/json');
                echo json_encode([
                    'success' => true,
                    'format' => 'txt',
                    'filename' => $downloadFilename,
                    'content' => $content
                ]);
            } else {
                // 对于压缩格式,直接输出文件
                header('Content-Type: ' . $contentType);
                header('Content-Disposition: attachment; filename="' . $downloadFilename . '"');
                header('Content-Length: ' . strlen($content));
                echo $content;
            }
            return true;
        }
    }
    
    return false;
}

// 主执行逻辑
try {
    // 首先处理AJAX请求
    if (handleAjaxRequest()) {
        exit;
    }
} catch (Exception $e) {
    header('Content-Type: application/json');
    echo json_encode([
        'success' => false,
        'message' => '服务器错误: ' . $e->getMessage()
    ]);
    exit;
}

// HTML界面保持不变...
?>

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>文件打包工具</title>
    <style>
        /* 样式保持不变... */
        :root {
            --primary-color: #3498db;
            --secondary-color: #2980b9;
            --success-color: #2ecc71;
            --danger-color: #e74c3c;
            --light-color: #f8f9fa;
            --dark-color: #343a40;
            --border-radius: 4px;
        }
        
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            line-height: 1.6;
            color: #333;
            background-color: #f5f7fa;
            padding: 20px;
        }
        
        .container {
            max-width: 1200px;
            margin: 0 auto;
            background: white;
            border-radius: var(--border-radius);
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
            padding: 30px;
        }
        
        h1, h2, h3 {
            color: var(--dark-color);
            margin-bottom: 20px;
        }
        
        h1 {
            text-align: center;
            margin-bottom: 30px;
            color: var(--primary-color);
        }
        
        .panel {
            background: var(--light-color);
            border-radius: var(--border-radius);
            padding: 20px;
            margin-bottom: 20px;
        }
        
        .file-list {
            max-height: 500px;
            overflow-y: auto;
            border: 1px solid #ddd;
            border-radius: var(--border-radius);
            padding: 15px;
            background: white;
        }
        
        .file-item {
            display: flex;
            align-items: center;
            padding: 8px 0;
            border-bottom: 1px solid #eee;
        }
        
        .file-item:last-child {
            border-bottom: none;
        }
        
        .file-item input[type="checkbox"] {
            margin-right: 10px;
        }
        
        .file-icon {
            margin-right: 8px;
            color: var(--primary-color);
        }
        
        .folder > .file-name {
            font-weight: bold;
            color: var(--secondary-color);
        }
        
        .folder .children {
            margin-left: 20px;
            display: none;
        }
        
        .folder.expanded .children {
            display: block;
        }
        
        .folder-toggle {
            cursor: pointer;
            margin-right: 5px;
            color: var(--secondary-color);
        }
        
        .options {
            display: flex;
            flex-wrap: wrap;
            gap: 20px;
            margin: 20px 0;
        }
        
        .option-group {
            flex: 1;
            min-width: 200px;
        }
        
        select, button, input[type="text"] {
            width: 100%;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: var(--border-radius);
            font-size: 16px;
        }
        
        button {
            background-color: var(--primary-color);
            color: white;
            border: none;
            cursor: pointer;
            transition: background-color 0.3s;
        }
        
        button:hover {
            background-color: var(--secondary-color);
        }
        
        button:disabled {
            background-color: #95a5a6;
            cursor: not-allowed;
        }
        
        .format-option {
            display: flex;
            align-items: center;
            margin-bottom: 10px;
        }
        
        .format-option input {
            margin-right: 10px;
        }
        
        .status {
            margin-top: 20px;
            padding: 15px;
            border-radius: var(--border-radius);
            display: none;
        }
        
        .status.success {
            background-color: #d4edda;
            color: #155724;
            border: 1px solid #c3e6cb;
        }
        
        .status.error {
            background-color: #f8d7da;
            color: #721c24;
            border: 1px solid #f5c6cb;
        }
        
        .status.info {
            background-color: #d1ecf1;
            color: #0c5460;
            border: 1px solid #bee5eb;
        }
        
        .progress {
            width: 100%;
            height: 20px;
            background-color: #e9ecef;
            border-radius: var(--border-radius);
            margin: 10px 0;
            overflow: hidden;
            display: none;
        }
        
        .progress-bar {
            height: 100%;
            background-color: var(--primary-color);
            width: 0%;
            transition: width 0.3s;
        }
        
        .stats {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
            gap: 15px;
            margin-top: 20px;
        }
        
        .stat-item {
            background: var(--light-color);
            padding: 15px;
            border-radius: var(--border-radius);
            text-align: center;
        }
        
        .stat-value {
            font-size: 24px;
            font-weight: bold;
            color: var(--primary-color);
        }
        
        .header-actions {
            display: flex;
            justify-content: space-between;
            margin-bottom: 20px;
            flex-wrap: wrap;
            gap: 10px;
        }
        
        .btn {
            padding: 8px 15px;
            border-radius: var(--border-radius);
            cursor: pointer;
            border: none;
            font-size: 14px;
        }
        
        .btn-primary {
            background-color: var(--primary-color);
            color: white;
        }
        
        .btn-secondary {
            background-color: #6c757d;
            color: white;
        }
        
        .preview-area {
            margin-top: 20px;
            border: 1px solid #ddd;
            border-radius: var(--border-radius);
            padding: 15px;
            background: white;
            display: none;
        }
        
        .preview-content {
            width: 100%;
            height: 300px;
            font-family: monospace;
            border: 1px solid #ddd;
            border-radius: var(--border-radius);
            padding: 10px;
            resize: vertical;
        }
        
        @media (max-width: 768px) {
            .options {
                flex-direction: column;
            }
            
            .header-actions {
                flex-direction: column;
            }
        }
    </style>
</head>
<body>
    <div>
        <h1>文件打包工具</h1>
        
        <div>
            <button id="select-all" class="btn btn-primary">全选</button>
            <button id="deselect-all" class="btn btn-secondary">取消全选</button>
            <button id="expand-all" class="btn btn-secondary">展开所有</button>
            <button id="collapse-all" class="btn btn-secondary">折叠所有</button>
        </div>
        
        <div>
            <h2>选择要打包的文件</h2>
            <div id="file-list">
                <div>
                    <span>正在加载文件列表...</span>
                </div>
            </div>
        </div>
        
        <div>
            <h2>打包选项</h2>
            <div>
                <div>
                    <h3>输出格式</h3>
                    <div id="format-options">
                        <div>
                            <input type="radio" name="format" id="format-txt" value="txt" checked>
                            <label for="format-txt">TXT (文本预览)</label>
                        </div>
                    </div>
                </div>
                
                <div>
                    <h3>输出文件名</h3>
                    <input type="text" id="output-filename" placeholder="请输入输出文件名" value="打包文件">
                </div>
            </div>
            
            <button id="generate-btn">生成文件</button>
            
            <div id="progress-container">
                <div id="progress-bar"></div>
            </div>
            
            <div id="status-message"></div>
            
            <div id="preview-area">
                <h3 id="preview-title">文件内容预览</h3>
                <textarea id="preview-content" readonly></textarea>
                <button id="download-preview" class="btn btn-primary" style="margin-top: 10px;">下载文件</button>
            </div>
            
            <div id="stats-container">
                <div>
                    <div>总文件数</div>
                    <div id="total-files">0</div>
                </div>
                <div>
                    <div>总文件夹数</div>
                    <div id="total-folders">0</div>
                </div>
                <div>
                    <div>已选择文件</div>
                    <div id="selected-count">0</div>
                </div>
                <div>
                    <div>支持格式</div>
                    <div id="supported-formats">1</div>
                </div>
            </div>
        </div>
    </div>

    <script>
        // 全局变量
        let fileStructure = [];
        let selectedFiles = [];
        let supportedFormats = [];
        
        // 页面加载完成后初始化
        document.addEventListener('DOMContentLoaded', function() {
            // 获取文件结构
            fetchFileStructure();
            
            // 绑定事件
            document.getElementById('select-all').addEventListener('click', selectAll);
            document.getElementById('deselect-all').addEventListener('click', deselectAll);
            document.getElementById('expand-all').addEventListener('click', expandAll);
            document.getElementById('collapse-all').addEventListener('click', collapseAll);
            document.getElementById('generate-btn').addEventListener('click', generateFile);
            document.getElementById('download-preview').addEventListener('click', downloadPreview);
        });
        
        // 获取文件结构
        function fetchFileStructure() {
            showStatus('正在扫描文件结构...', 'info');
            
            // 使用AJAX获取文件结构
            fetch('?action=scan')
                .then(response => {
                    if (!response.ok) {
                        throw new Error('网络响应不正常');
                    }
                    return response.json();
                })
                .then(data => {
                    if (data.success) {
                        fileStructure = data.fileStructure;
                        supportedFormats = data.supportedFormats;
                        
                        // 渲染文件列表
                        renderFileList();
                        
                        // 渲染格式选项
                        renderFormatOptions();
                        
                        // 显示统计信息
                        showStats();
                        
                        showStatus('文件结构扫描完成', 'success');
                    } else {
                        throw new Error(data.message || '扫描失败');
                    }
                })
                .catch(error => {
                    console.error('Error:', error);
                    showStatus('扫描文件结构时出错: ' + error.message, 'error');
                });
        }
        
        // 渲染文件列表
        function renderFileList() {
            const fileListContainer = document.getElementById('file-list');
            fileListContainer.innerHTML = '';
            
            if (fileStructure.length === 0) {
                fileListContainer.innerHTML = '<div><span>没有找到文件或目录不可读</span></div>';
                return;
            }
            
            fileStructure.forEach(item => {
                fileListContainer.appendChild(createFileItem(item));
            });
        }
        
        // 创建文件项
        function createFileItem(item, level = 0) {
            const itemDiv = document.createElement('div');
            itemDiv.className = 'file-item';
            itemDiv.style.paddingLeft = (level * 20) + 'px';
            
            if (item.type === 'folder') {
                itemDiv.classList.add('folder');
                
                const toggleSpan = document.createElement('span');
                toggleSpan.className = 'folder-toggle';
                toggleSpan.innerHTML = '▶';
                toggleSpan.addEventListener('click', function() {
                    itemDiv.classList.toggle('expanded');
                    toggleSpan.innerHTML = itemDiv.classList.contains('expanded') ? '▼' : '▶';
                });
                
                const checkbox = document.createElement('input');
                checkbox.type = 'checkbox';
                checkbox.id = 'item-' + item.path;
                checkbox.dataset.path = item.path;
                checkbox.dataset.type = 'folder';
                checkbox.addEventListener('change', handleFolderSelection);
                
                const iconSpan = document.createElement('span');
                iconSpan.className = 'file-icon';
                iconSpan.innerHTML = '📁';
                
                const nameSpan = document.createElement('span');
                nameSpan.className = 'file-name';
                nameSpan.textContent = item.name;
                
                itemDiv.appendChild(toggleSpan);
                itemDiv.appendChild(checkbox);
                itemDiv.appendChild(iconSpan);
                itemDiv.appendChild(nameSpan);
                
                // 添加子项容器
                const childrenDiv = document.createElement('div');
                childrenDiv.className = 'children';
                
                if (item.children && item.children.length > 0) {
                    item.children.forEach(child => {
                        childrenDiv.appendChild(createFileItem(child, level + 1));
                    });
                }
                
                itemDiv.appendChild(childrenDiv);
            } else {
                const checkbox = document.createElement('input');
                checkbox.type = 'checkbox';
                checkbox.id = 'item-' + item.path;
                checkbox.dataset.path = item.path;
                checkbox.dataset.type = 'file';
                checkbox.addEventListener('change', handleFileSelection);
                
                const iconSpan = document.createElement('span');
                iconSpan.className = 'file-icon';
                iconSpan.innerHTML = getFileIcon(item.name);
                
                const nameSpan = document.createElement('span');
                nameSpan.className = 'file-name';
                nameSpan.textContent = item.name;
                
                // 添加文件大小
                if (item.size !== undefined) {
                    const sizeSpan = document.createElement('span');
                    sizeSpan.style.marginLeft = '10px';
                    sizeSpan.style.color = '#666';
                    sizeSpan.style.fontSize = '0.9em';
                    sizeSpan.textContent = formatFileSize(item.size);
                    nameSpan.appendChild(sizeSpan);
                }
                
                itemDiv.appendChild(checkbox);
                itemDiv.appendChild(iconSpan);
                itemDiv.appendChild(nameSpan);
            }
            
            return itemDiv;
        }
        
        // 获取文件图标
        function getFileIcon(filename) {
            const ext = filename.split('.').pop().toLowerCase();
            const iconMap = {
                'php': '🐘',
                'html': '🌐',
                'css': '🎨',
                'js': '📜',
                'json': '📋',
                'txt': '📄',
                'md': '📝',
                'xml': '📰',
                'sql': '🗄️',
                'jpg': '🖼️',
                'jpeg': '🖼️',
                'png': '🖼️',
                'gif': '🖼️',
                'pdf': '📕',
                'zip': '📦',
                'rar': '📦',
                '7z': '📦'
            };
            
            return iconMap[ext] || '📄';
        }
        
        // 格式化文件大小
        function formatFileSize(bytes) {
            if (bytes === 0) return '0 B';
            const k = 1024;
            const sizes = ['B', 'KB', 'MB', 'GB'];
            const i = Math.floor(Math.log(bytes) / Math.log(k));
            return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
        }
        
        // 处理文件夹选择
        function handleFolderSelection(event) {
            const checkbox = event.target;
            const folderPath = checkbox.dataset.path;
            const isChecked = checkbox.checked;
            
            // 找到所有属于这个文件夹的子项
            const children = document.querySelectorAll(`[data-path^="${folderPath}/"]`);
            
            children.forEach(child => {
                child.checked = isChecked;
                if (child.dataset.type === 'file') {
                    updateSelectedFiles(child.dataset.path, isChecked);
                }
            });
            
            updateSelectedFiles(folderPath, isChecked);
        }
        
        // 处理文件选择
        function handleFileSelection(event) {
            const checkbox = event.target;
            const filePath = checkbox.dataset.path;
            const isChecked = checkbox.checked;
            
            updateSelectedFiles(filePath, isChecked);
        }
        
        // 更新选中的文件列表
        function updateSelectedFiles(path, isSelected) {
            if (isSelected) {
                if (!selectedFiles.includes(path)) {
                    selectedFiles.push(path);
                }
            } else {
                const index = selectedFiles.indexOf(path);
                if (index > -1) {
                    selectedFiles.splice(index, 1);
                }
            }
            
            updateStats();
        }
        
        // 全选
        function selectAll() {
            const checkboxes = document.querySelectorAll('#file-list input[type="checkbox"]');
            checkboxes.forEach(checkbox => {
                checkbox.checked = true;
                if (checkbox.dataset.type === 'file') {
                    updateSelectedFiles(checkbox.dataset.path, true);
                }
            });
            
            updateStats();
        }
        
        // 取消全选
        function deselectAll() {
            const checkboxes = document.querySelectorAll('#file-list input[type="checkbox"]');
            checkboxes.forEach(checkbox => {
                checkbox.checked = false;
                if (checkbox.dataset.type === 'file') {
                    updateSelectedFiles(checkbox.dataset.path, false);
                }
            });
            
            updateStats();
        }
        
        // 展开所有
        function expandAll() {
            const folders = document.querySelectorAll('.folder');
            const toggles = document.querySelectorAll('.folder-toggle');
            
            folders.forEach(folder => {
                folder.classList.add('expanded');
            });
            
            toggles.forEach(toggle => {
                toggle.innerHTML = '▼';
            });
        }
        
        // 折叠所有
        function collapseAll() {
            const folders = document.querySelectorAll('.folder');
            const toggles = document.querySelectorAll('.folder-toggle');
            
            folders.forEach(folder => {
                folder.classList.remove('expanded');
            });
            
            toggles.forEach(toggle => {
                toggle.innerHTML = '▶';
            });
        }
        
        // 渲染格式选项
        function renderFormatOptions() {
            const formatContainer = document.getElementById('format-options');
            formatContainer.innerHTML = '';
            
            supportedFormats.forEach(format => {
                const optionDiv = document.createElement('div');
                optionDiv.className = 'format-option';
                
                const radio = document.createElement('input');
                radio.type = 'radio';
                radio.name = 'format';
                radio.id = 'format-' + format;
                radio.value = format;
                if (format === 'txt') radio.checked = true;
                
                const label = document.createElement('label');
                label.htmlFor = 'format-' + format;
                label.textContent = format.toUpperCase() + (format === 'txt' ? ' (文本预览)' : ' (直接下载)');
                
                optionDiv.appendChild(radio);
                optionDiv.appendChild(label);
                formatContainer.appendChild(optionDiv);
            });
            
            // 更新支持的格式数量
            document.getElementById('supported-formats').textContent = supportedFormats.length;
        }
        
        // 生成文件
        function generateFile() {
            if (selectedFiles.length === 0) {
                showStatus('请至少选择一个文件', 'error');
                return;
            }
            
            const format = document.querySelector('input[name="format"]:checked').value;
            const filename = document.getElementById('output-filename').value || '打包文件';
            
            showStatus('正在生成文件...', 'info');
            document.getElementById('progress-container').style.display = 'block';
            document.getElementById('preview-area').style.display = 'none';
            
            // 模拟进度条
            let progress = 0;
            const progressBar = document.getElementById('progress-bar');
            const progressInterval = setInterval(() => {
                progress += 5;
                progressBar.style.width = progress + '%';
                
                if (progress >= 100) {
                    clearInterval(progressInterval);
                    
                    // 实际生成文件
                    fetch('', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json',
                        },
                        body: JSON.stringify({
                            action: 'generate',
                            files: selectedFiles,
                            format: format,
                            filename: filename
                        })
                    })
                    .then(response => {
                        if (format === 'txt') {
                            return response.json();
                        } else {
                            if (!response.ok) {
                                return response.json().then(err => { throw new Error(err.message); });
                            }
                            return response.blob();
                        }
                    })
                    .then(data => {
                        if (format === 'txt') {
                            if (data.success) {
                                // 对于TXT文件,显示内容预览
                                showFilePreview(data.content, data.filename);
                                showStatus('文件生成成功,请查看预览', 'success');
                            } else {
                                throw new Error(data.message);
                            }
                        } else {
                            // 对于压缩文件,直接下载
                            downloadFile(data, `${filename}.${format === 'gz' ? 'tar.gz' : format}`);
                            showStatus('文件已生成并开始下载', 'success');
                        }
                        
                        document.getElementById('progress-container').style.display = 'none';
                    })
                    .catch(error => {
                        console.error('Error:', error);
                        showStatus('生成文件时出错: ' + error.message, 'error');
                        document.getElementById('progress-container').style.display = 'none';
                    });
                }
            }, 100);
        }
        
        // 显示文件内容预览(TXT格式)
        function showFilePreview(content, filename) {
            const previewArea = document.getElementById('preview-area');
            const previewTitle = document.getElementById('preview-title');
            const previewContent = document.getElementById('preview-content');
            
            previewTitle.textContent = filename + ' 内容预览';
            previewContent.value = content;
            previewArea.style.display = 'block';
            
            // 滚动到预览区域
            previewArea.scrollIntoView({ behavior: 'smooth' });
        }
        
        // 下载预览文件
        function downloadPreview() {
            const content = document.getElementById('preview-content').value;
            const filename = document.getElementById('output-filename').value || '打包文件';
            downloadTxt(content, filename + '.txt');
        }
        
        // 下载TXT文件
        function downloadTxt(content, filename) {
            const blob = new Blob([content], { type: 'text/plain; charset=utf-8' });
            downloadFile(blob, filename);
        }
        
        // 下载文件
        function downloadFile(blob, filename) {
            const url = window.URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = filename;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            window.URL.revokeObjectURL(url);
        }
        
        // 显示状态消息
        function showStatus(message, type) {
            const statusDiv = document.getElementById('status-message');
            statusDiv.textContent = message;
            statusDiv.className = `status ${type}`;
            statusDiv.style.display = 'block';
            
            // 自动隐藏成功消息
            if (type === 'success') {
                setTimeout(() => {
                    statusDiv.style.display = 'none';
                }, 5000);
            }
        }
        
        // 显示统计信息
        function showStats() {
            const totalFiles = countFiles(fileStructure);
            const totalFolders = countFolders(fileStructure);
            
            document.getElementById('total-files').textContent = totalFiles;
            document.getElementById('total-folders').textContent = totalFolders;
            updateStats();
        }
        
        // 更新统计信息
        function updateStats() {
            const selectedCount = document.getElementById('selected-count');
            selectedCount.textContent = selectedFiles.length;
        }
        
        // 计算文件数量
        function countFiles(structure) {
            let count = 0;
            
            structure.forEach(item => {
                if (item.type === 'file') {
                    count++;
                } else if (item.type === 'folder' && item.children) {
                    count += countFiles(item.children);
                }
            });
            
            return count;
        }
        
        // 计算文件夹数量
        function countFolders(structure) {
            let count = 0;
            
            structure.forEach(item => {
                if (item.type === 'folder') {
                    count++;
                    if (item.children) {
                        count += countFolders(item.children);
                    }
                }
            });
            
            return count;
        }
    </script>
</body>
</html>


欢迎您,来自美国的朋友,您的IP:216.73.216.136,您的网络:


服务热线

1888888888

要发发发发发发

站长微信公众号

站长微信公众号

分享:

支付宝

微信