public function importGoodsDetail(): Json
{
set_time_limit(0);
ini_set('memory_limit', '2048M');
$params = $this->request->param();
if (empty($params["file"])) {
return $this->fail('请上传要导入的文件',[],0,1);
}
$titleArr = [
"物流履约单号" => "logistics_order_no",
"货品名称" => "product_name",
"货品ID" => "product_id",
"货品件数" => "quantity",
"货品重量(克)" => "weight",
"货品长度(毫米)" => "length",
"货品宽度(毫米)" => "width",
"货品高度(毫米)" => "height",
"货品体积(立方毫米)" => "volume",
];
try {
// 使用生成器获取数据流
$dataGenerator = $this->importExeclGenerator($params['file']);
$batchSize = 3000; // 每批处理300条,可根据服务器性能调整
$model = new GoodsDetailModel();
$total = 0;
$success = 0;
$failedBatches = []; // 记录失败的批次
// 直接处理数据,不要使用事务
$batchData = [];
foreach ($dataGenerator as $rowData) {
$total++;
// 转换数据格式
$item = [];
foreach ($rowData as $k => $val) {
if ($k && $val && isset($titleArr[$k])) {
$item[$titleArr[$k]] = trim($val);
}
}
if (!empty($item)) {
$batchData[] = $item;
}
// 达到批次大小或处理完所有数据时插入数据库
if (count($batchData) >= $batchSize) {
if (!empty($batchData)) {
try {
$model->saveAll($batchData);
$success += count($batchData);
} catch (\Exception $e) {
// 记录失败批次(可选)
$failedBatches[] = [
'startRow' => $total - count($batchData) + 1,
'endRow' => $total,
'error' => $e->getMessage()
];
}
}
$batchData = []; // 清空批次数据
}
}
// 处理剩余数据
if (!empty($batchData)) {
try {
//TODO:有优化的空间 速度可以大大提升 赶进度暂且这样...
$model->saveAll($batchData);
$success += count($batchData);
} catch (\Exception $e) {
$failedBatches[] = [
'startRow' => $total - count($batchData) + 1,
'endRow' => $total,
'error' => $e->getMessage()
];
}
}
// 构建返回结果
$resultMsg = "导入成功,共{$total}条数据,成功导入{$success}条";
if (!empty($failedBatches)) {
$resultMsg .= ",失败 ".$total - $success." 条,具体报错信息:".$e->getMessage();
return $this->fail('导入失败',['remark' => $resultMsg],1,1);
}
return $this->success('导入成功',['remark' => $resultMsg],1,1);
} catch (\Exception $e) {
return $this->fail('导入失败',['remark' => $e->getMessage()],1,1);
}
}
上面这是我的php后端用于导入文件,其中调用导入的方法如下:
private function importExeclGenerator(string $file_name): \Generator
{
try {
$path = app()->getRootPath() . "public/" . $file_name;
$reader = IOFactory::createReaderForFile($path);
$reader->setReadDataOnly(true);
$spreadsheet = $reader->load($path);
// 获取第一个工作表
$sheet = $spreadsheet->getSheet(0);
// 获取表头
$title = $sheet->rangeToArray('A1:' . $sheet->getHighestColumn() . '1', null, true, true, true)[1];
if (empty($title)) {
throw new \Exception('Excel表头为空');
}
$highestRow = $sheet->getHighestRow();
// 逐行生成数据,不一次性加载到内存
for ($row = 2; $row <= $highestRow; $row++) {
$rowData = $sheet->rangeToArray(
'A' . $row . ':' . $sheet->getHighestColumn() . $row,
null,
true,
true,
true
)[$row];
$item = [];
foreach ($rowData as $colIndex => $value) {
$colName = $title[$colIndex];
if ($colName) {
//$item[$colName] = trim($value);
$item[$colName] = $value;
}
}
// 使用yield关键字逐行返回数据
yield $item;
}
} catch (\Exception $e) {
throw $e;
}
}
我是使用了流的形式导入,但是报错502网关超时,我的前端代码如下:
//菜鸟仓储费--正向配送费--货品明细导入api
export function importGoodsDetail(params:any) {
return request.post({
url:'/wareHousFee.newFie.forwardDeliveryFee.GoodsDetail/importGoodsDetail',
params,
// 我们的标准是100万数据导出 设置超时时间为5小时 (5 * 60 * 60 * 1000 = 18,000,000毫秒)
timeout:18000000
})
}
<template>
<el-dialog :title="upload.title" v-model="show" width="400px">
<el-upload ref="uploadRefs" :limit="1" accept=".xlsx, .xls" :timeout="120000" :max-size="200*1024*1024" :headers="upload.headers" :action="upload.url" :on-progress="handleFileUploadProgress" :on-success="handleFileSuccess" :auto-upload="true" :on-error="uploadErr" drag>
<i class="el-icon-upload"></i>
<div class="el-upload__text">将文件拖到此处,或点击上传</div>
<div class="el-upload__tip text-center" slot="tip">
<span>仅允许导入xls、xlsx格式文件。</span>
<!-- <el-link type="primary" :underline="false" style="font-size: 12px; vertical-align: baseline"
v-hasPermi="['teacher:import:muban']" @click="importTemplate">下载模板</el-link> -->
</div>
</el-upload>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitFileForm" :disabled="submitLoading">确 定</el-button>
<el-button @click="handleClose">取 消</el-button>
</div>
</el-dialog>
</template>
<script lang="ts" setup >
import { getToken } from '@/utils/auth'
import feedback from '@/utils/feedback'
import config from '@/config'
import type { ElUpload } from 'element-plus'
import { importGoodsDetail } from '@/api/wareHousFee/newBie'
import { useRoute } from 'vue-router'
const emit = defineEmits(['success'])
const route = useRoute()
const show = ref(false);
const uploadRefs = shallowRef<InstanceType<typeof ElUpload>>()
const upload: any = ref({
title: "正向配送费货品明细导入",
open: false,
url: ref(`${config.baseUrl}${config.urlPrefix}/upload/excel`),
headers: {
token: getToken(),
version: config.version
}
});
const fileUrl=ref("");
var source= route.query.source;
// 控制按钮加载状态
const submitLoading = ref(false);
//下载导入模版 未使用
const importTemplate = async () => {}
//文件上传进度的回调函数
const handleFileUploadProgress = async (response: any, file: any, fileLists: any[]) => {}
//文件上传成功的回调函数
const handleFileSuccess = async (response: any, file: any, fileLists: any[]) => {
fileUrl.value=response.data.url
}
//文件上传失败的回调函数
// 文件上传失败的回调函数
const uploadErr = (error: any, file: any, fileList: any[]) => {
console.log('[上传失败] 错误触发');
// 区分不同类型的错误
if (error instanceof Error) {
console.log('[上传失败] 错误对象:', error);
console.log('[上传失败] 错误信息:', error.message);
} else {
console.log('[上传失败] 响应数据:', error);
// 尝试解析可能的HTTP状态码
if (error.response && error.response.status) {
console.log('[上传失败] 状态码:', error.response.status);
console.log('[上传失败] 状态文本:', error.response.statusText);
}
}
console.log('[上传失败] 文件信息:', {
name: file.name,
size: `${(file.size / 1024 / 1024).toFixed(2)}MB`,
type: file.type
});
}
//上传组件确定按钮操作
const submitFileForm = async () => {
//防止重复点击确定按钮
if (submitLoading.value) return;
feedback.loading('正在导入中...')
try {
//设置确定按钮禁用
submitLoading.value = true;
// 调用后端接口并获取返回结果
const response = await importGoodsDetail({ file: fileUrl.value });
feedback.closeLoading()
// 控制台打印返回结果(便于调试)
console.log("接口返回结果:", response);
emit("success");
show.value = false;
} catch (error) {
// 接口调用异常(如网络错误、超时等)
//console.error("接口调用异常:", error);
feedback.msgError("导入失败,接口调用异常,请稍后再试");
feedback.closeLoading()
} finally {
//无论成功或者失败都设置按钮可点击
submitLoading.value = false;
feedback.closeLoading()
}
}
//点击弹窗关闭按钮
const handleClose = () => {
fileUrl.value = ''
show.value = false
}
//点击按钮除非open事件打开弹窗
const open = (source:any) => {
show.value = true
}
//defineExpose({ open }) 的核心作用是将子组件的 open 方法显式暴露给父组件
defineExpose({
open
})
</script>
请帮我具体排查一下接口是不是写的有问题啊
最新发布