php 异步断点继续导出大文件

该博客介绍了如何通过PHP实现断点续传下载的功能。首先,当用户请求下载时,系统将查询语句存入数据库,并开启定时任务生成CSV文件。在生成文件过程中,按页分批查询数据并写入临时文件,同时更新数据库中的进度信息。文件生成完成后,进行文件合并,并清理临时文件。整个过程支持断点续传,确保下载的完整性和效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1点击下载时将查询语句存入数据库


  $this->db->insert('excel_export', [
            'file_name' => 'orderInfo' . date('YmdHis'),
            'path' => 'storage/excel_temp/',
            'uid' => $user['uid'],
            'type' => 1,
            'created_at' => date('Y-m-d H:i:s'),
            'count' => $count,
            'sql' => str_replace('LIMIT 500 OFFSET 0', '', $this->db->log()[0]),
            'number' => 0,
            'rate' => 0,
        ]);

在这里插入图片描述
2开启定时任务生成文件并合并

//生成文件
    public function createExcel()
    {
        ExcelExport::where('status', 0)->get()->take(1)->map(function ($item) {
            if ($item['type'] == 1 && $item['sql'] != null) {
                $this->exportOrder($item);
            }
        });
    }

    public function exportOrder($item)
    {
        $num = 0;
        $f = null;
        foreach ($this->getCounts($item, function ($fp) use (&$f) {
            $f = $fp;
        }) as $value) {
            $num++;
            if (1000 === $num) {
                ob_flush();
                flush();
                $num = 0;
            }
            foreach ($value as $v) {
                $put = json_decode(json_encode($v), true);
                fputcsv($f, $put);
            }
            fclose($f);
        }
        $this->mergeFile($item, function ($file) use ($item) {
            error_reporting(0);
            $path = public_path('files/excel_final');
            if (!file_exists($path)) {
                mkDirs($path);
            }
            if (file_exists($file)) {
                $newFile = $path . '/' . $item['file_name']; //新目录
                copy($file, $newFile); //拷贝到新目录
            }
            self::clearFile($item);
        });
    }

    /**
     * 获取阶段导出数据
     */
    private function getCounts($item, $handle)
    {

        $limit = 500;
        $pageCount = ceil($item['count'] / $limit);
        for ($i = 0; $i < $pageCount; $i++) {
            if ($i * $limit >= $item['number']) {
                if (!file_exists(public_path('files/excel_temp/' . $item['file_name']))) {
                    mkDirs(public_path('files/excel_temp/' . $item['file_name']));
                }

                $file = public_path('files/excel_temp/' . $item['file_name'] . '/_' . $i . '.csv');
                touch($file);
                $fp = fopen($file, 'w'); //生成临时文件
                if ($handle instanceof \closure) {
                    $handle($fp);
                }
                $new_list = [];
                $order = DB::select($item['sql'] . ' LIMIT ' . $limit . ' OFFSET ' . $i * $limit);

                $order_track = DB::table('sync_order_track')
                    ->leftJoin('enum', 'enum.key', '=', 'sync_order_track.status')
                    ->where([
                        'enum.type' => 'track_status',
                    ])
                    ->whereIn('source_order_id', collect($order)->pluck('source_order_id'))
                    ->get();
                collect($order)->map(function ($item) use (&$new_list, $order_track) {
                    $row = json_decode($item->raw, true);
                    $track = collect($order_track)->where('source_order_id', $item->source_order_id)->sortBy('flush_date')->last();
                    $new_list[] = [
                        'id' => $item->id,
                        'shop_name' => $item->shop_name,
                        'products' => join(',', array_column($row['products'], 'title')),
                        'source_order_id' => $item->source_order_id,
                        'ship_name' => $item->ship_name,
                        'tracking_number' => $item->tracking_number,
                        'ship_time' => $item->ship_time,
                        'status' => $item->status,
                        'money' => $row['subtotal_price'] . $row['currency'],
                        'money_status' => $item->money_time == null ? '未回款' : '已回款',
                        'money_time' => $item->money_time,
                        'ship_current_status' => $item->ship_current_status,
                        'track_message' => $track->value ?? '',
                        'qs_time' => $track->flush_date ?? '',
                        'plat_checkout_at' => $item->plat_checkout_at,
                    ];
                });
                $update = [
                    'number' => ($i + 1) * $limit > $item['count'] ? $item['count'] : ($i + 1) * $limit,
                    'rate' => ($i + 1) * $limit / $item['count']
                ];
                if ($update['rate'] >= 1) {
                    $update['status'] = 1;
                    $update['rate'] = 1;
                    $update['path'] = public_path('files/excel_final') . '/' . $item['file_name'];
                }
                ExcelExport::where('id', $item['id'])->update($update);
                yield $new_list;
            }
        }
    }

    /**
     * 合并文件
     */
    private function mergeFile($item, $handle)
    {
        $fileList = glob(public_path('files/excel_temp/' . $item['file_name'] . '/*'));
        $count = count($fileList);
        for ($i = 0; $i < $count; $i++) {
            if ($i > 0) {
                file_put_contents($fileList[0], file_get_contents($fileList[$i]), FILE_APPEND);
            }
        }
        if ($handle instanceof \closure) {
            $handle($fileList[0]);
        }
    }

    /**
     * 清除文件
     */
    private static function clearFile($item)
    {
        error_reporting(0);
        $fileList = glob(public_path('files/excel_temp/' . $item['file_name'] . '/*'));
        foreach ($fileList as $value) {
            unlink($value);
        }
    }

3前端查看下载进度 完成导出的可下载,以上方法可实现断点继续下载
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值