vue 文件上传 读取文件数据进行格式校验

概要

文件上传这里用的是 element-Ui  的 el-upload  在文件上传前 前端读取文件内容 进行内容数据格式的校验 通过才可以进行上传  否则给出 异常提示

整体架构流程代码

 <el-upload
        :on-success="onSucess"
        :on-error="uploadError"
        :before-upload="beforeUpload"
        :multiple="false"
        class="upload-demo"
        action="/api/ZTMM/ntmm-mmacct/upload"
        :headers="headersParam"
        :data="tbmmacct"
        :show-file-list="false"
      >
        <el-tooltip
          class="item"
          effect="light"
          content="请上传.xls格式文件"
          placement="right"
        >
          <el-button size="small" v-if="uploadBtn"
            ><span style="color: #66b1ff">选取文件</span></el-button
          >
        </el-tooltip>
      </el-upload>

   beforeUpload(file) {
      this.loading = true;
      const isXLS = file.type === "application/vnd.ms-excel";
      if (!isXLS) {
        this.$message.error("上传文件只能是xls格式!");
        this.loading = false;
        return false;
      }
      // this.loading = true;
      // 使用 FileReader 读取文件内容进行校验
      const reader = new FileReader();
      reader.onload = async (e) => {
        const fileContent = e.target.result;

        // 解析 Excel 文件内容
        const workbook = XLSX.read(fileContent, { type: "array" });
        const sheet = workbook.Sheets[workbook.SheetNames[0]]; // 获取第一个 sheet
        const data = XLSX.utils.sheet_to_json(sheet); // 将 sheet 转为 JSON 数组

        // 进行自定义的文件内容校验
        if (await this.validateFileContent(data)) {
          this.$message.success("文件内容校验通过");
          this.loading = false;
          this.uploadFile(file);
        } else {
          this.$message.error("文件为空或内容格式不符合要求, 请重新检查上传!");
          this.loading = false;
          return false; // 校验失败,阻止上传
        }
      };

      // 读取文件内容
      reader.readAsArrayBuffer(file);
      // 返回 false,表示文件上传暂时被阻止,等待校验完成
      return false;
    },

    uploadFile(file) {
      // 手动触发上传,这里可以通过修改 `action` 或直接调用 `upload` 方法上传
      const formData = new FormData();
      formData.append("file", file);

      // 进行自定义的上传请求
      this.$axios
        .post("/api/ZTMM/ntmm-mmacct/upload", formData, {
          headers: {
            "Content-Type": "multipart/form-data",
            ...this.headersParam, // 如果有额外的头部信息
          },
          params: this.tbmmacct, // 如果有额外的请求数据
        })
        .then((response) => {
          this.$message.success("文件上传成功");
          this.getFiles();
          // 处理成功逻辑
        })
        .catch((error) => {
          this.$message.error("文件上传失败");
          // 处理错误逻辑
        });
    },
    async validateFileContent(data) {
      console.log(data, "daat");

      if (data && data.length > 0) {
        let newArr = data.slice(1);
        // 转换数据格式
        const transformedData = newArr.map((item) => ({
          itemId: item["盘条到货记录导入模版"],
          itemName: item["__EMPTY"],
          arrivalQuantity: item["__EMPTY_1"],
          expectedArrivalTime: item["__EMPTY_2"],
          creUser: item["__EMPTY_3"],
          creTime: item["__EMPTY_4"],
        }));
        return this.validateData(transformedData);
      }
      return false; // 如果文件没有符合的内容,返回 false
    },
    validateData(data) {
      if (data.length === 0) {
        return false;
      }

      for (const item of data) {
        // 校验 itemId
        if (
          typeof item.itemId !== "string" ||
          item.itemId.length < 1 ||
          item.itemId.length > 14 ||
          item.itemId === null ||
          item.itemId === undefined
        ) {
          return false;
        }

        // 校验 itemName
        if (
          typeof item.itemName !== "string" ||
          item.itemName.length < 1 ||
          item.itemName.length > 10 ||
          item.itemName === null ||
          item.itemName === undefined
        ) {
          return false;
        }

        // 校验 arrivalQuantity
        const arrivalQuantity = parseInt(item.arrivalQuantity, 10);
        if (
          isNaN(arrivalQuantity) ||
          arrivalQuantity < 1 ||
          item.arrivalQuantity.length < 1 ||
          item.arrivalQuantity.length > 2 ||
          item.arrivalQuantity === null ||
          item.arrivalQuantity === undefined
        ) {
          return false;
        }

        // 校验 expectedArrivalTime
        const expectedArrivalTime = new Date(item.expectedArrivalTime);
        if (
          isNaN(expectedArrivalTime.getTime()) ||
          item.expectedArrivalTime === null ||
          item.expectedArrivalTime === undefined
        ) {
          return false;
        }

        // // 校验 creUser
        // if (typeof item.creUser !== "string" || item.creUser.length < 1 || item.creUser === null || item.creUser === undefined) {
        //   return false;
        // }

        // // 校验 creTime
        // if (typeof item.creTime !== "string" || item.creTime.length < 1 || item.creTime === null || item.creTime === undefined) {
        //   return false;
        // }
      }
      return true; // 所有校验通过
    },

技术细节

主要是在  beforeUpload 属性方法里实现 文件的校验与抓取,

 // 使用 FileReader 读取文件内容进行校验

      const reader = new FileReader();

      reader.onload = async (e) => {

        const fileContent = e.target.result;

        // 解析 Excel 文件内容

        const workbook = XLSX.read(fileContent, { type: "array" });

        const sheet = workbook.Sheets[workbook.SheetNames[0]]; // 获取第一个 sheet

        const data = XLSX.utils.sheet_to_json(sheet); // 将 sheet 转为 JSON 数组

        // 进行自定义的文件内容校验

        if (await this.validateFileContent(data)) {

          this.$message.success("文件内容校验通过");

          this.loading = false;

          this.uploadFile(file);

        } else {

          this.$message.error("文件为空或内容格式不符合要求, 请重新检查上传!");

          this.loading = false;

          return false; // 校验失败,阻止上传

        }

      };

      // 读取文件内容

      reader.readAsArrayBuffer(file);

      // 返回 false,表示文件上传暂时被阻止,等待校验完成

      return false;

    校验成功后需要手动去 抛送一下 uploadFile 以上代码有展示。

小结

组件结构

  1. <el-upload> 组件

    • :on-success:on-error 用于处理上传成功和失败的回调。
    • :before-upload 是一个钩子函数,在文件上传之前进行校验。
    • :multiple 设置为 false,表示只允许上传一个文件。
    • action 指定文件上传的接口地址。
    • :headers:data 用于传递额外的请求头和数据。
    • :show-file-list 设置为 false,表示不显示文件列表。
  2. <el-tooltip><el-button>

    • 提供用户界面,提示用户上传 .xls 格式的文件。

方法解析

  1. beforeUpload(file)

    • 在文件上传之前被调用。
    • 检查文件类型是否为 .xls,如果不是,则显示错误信息并停止上传。
    • 使用 FileReader 读取文件内容,并在读取完成后解析 Excel 文件。
    • 调用 validateFileContent(data) 方法对文件内容进行校验,如果通过,则调用 uploadFile(file) 方法进行上传。
  2. uploadFile(file)

    • 创建一个 FormData 对象,将文件添加到其中。
    • 使用 Axios 发送 POST 请求上传文件,并处理成功和失败的回调。
  3. validateFileContent(data)

    • 对解析后的 Excel 数据进行校验。
    • 将数据转换为特定格式,并调用 validateData(transformedData) 方法进行详细校验。
  4. validateData(data)

    • 对每一项数据进行校验,包括 itemIditemNamearrivalQuantityexpectedArrivalTime
    • 检查数据的类型、长度和有效性,确保符合要求。

总结

代码实现了一个完整的文件上传功能,包含了文件类型检查、内容校验和上传请求。通过这些方法,确保用户上传的文件符合预期的格式和内容要求,从而提高了数据的准确性和可靠性。

<think>好的,用户想了解如何通过Vue和Python实现文件上传读取文件内容并存储的功能。我需要结合他们提供的引用内容,特别是引用[1]和引用[2],来构建一个完整的解决方案。首先,我需要明确用户的需求:前端使用Vue上传文件,后端使用Python(可能是Django或Flask)处理上传文件读取内容并存储。 首先,前端的部分。根据引用[1],使用Vue创建文件上传表单。可能需要使用HTML的input元素类型为file,然后通过Vue的v-model绑定选择的文件。然后,当用户点击上传按钮时,通过axios或fetch API将文件发送到后端。需要注意的是,前端需要将文件包装在FormData对象中,以便正确传输。同时,可能需要显示上传进度,这可以通过axios的上传进度事件来实现。此外,前端还需要处理上传成功或失败的情况,给出相应的提示。 接下来是后端部分。引用[1]提到使用Django处理文件上传,但用户的问题中也提到了Python,可能不限于Django。如果用户更倾向于轻量级框架,可以考虑使用Flask。需要设置一个接收POST请求的路由,处理上传文件。在Django中,需要使用request.FILES获取上传文件对象,而在Flask中则是通过request.files。然后,需要将文件保存到服务器的指定位置,比如使用Django的FileSystemStorage或者直接使用Python的文件操作。 然后,读取文件内容。根据引用[2],如果上传的是ZIP文件,可以使用Python的zipfile模块来读取其中的内容。例如,用户上传了一个ZIP文件,后端可以解压或者直接读取其中的文件内容。如果是普通文本文件,比如txt或csv,可以直接读取内容并存储到数据库。如果是其他格式,可能需要不同的处理方式,比如使用pandas处理Excel或CSV文件。 存储方面,可能需要将文件内容存储到数据库中。例如,使用Django的模型来保存文件信息和内容,或者将文件存储在服务器的文件系统中,并在数据库中记录路径。如果是读取ZIP中的文件内容,可能需要将每个文件的内容提取出来,分别存储到数据库的相关表中。 需要考虑安全性问题,比如文件类型验证,防止上传恶意文件前端可以做一些基本的验证,比如文件类型和后缀,但后端也必须进行严格的检查。此外,处理大文件时需要考虑内存管理和分块上传,避免服务器过载。 用户可能还希望了解整个流程的步骤,所以需要分步说明,从前端到后端,再到存储。可能需要提供代码示例,比如Vue组件和Python后端代码,以及数据库操作的示例。同时,可能需要解释如何配置Vue和Django/Flask之间的跨域问题,确保前端能正确发送请求到后端。 另外,引用[2]中提到了使用zipfile模块读取ZIP文件内容,这在处理压缩文件时非常有用。用户可能需要知道如何将这一部分集成到后端处理流程中,比如上传ZIP文件后自动解压并读取内容存储。 可能还需要考虑异步处理,特别是当文件较大时,后端可能需要使用Celery等工具异步处理文件,避免阻塞请求线程。但用户的问题可能暂时不需要这么复杂,所以可以先提供同步处理的方案,再在优化部分提到异步处理的可能性。 最后,总结实现步骤,确保逻辑清晰,用户能够一步步跟随实现。同时,需要验证代码的正确性,确保示例代码能够运行,没有语法错误。可能还需要提到测试方法,比如使用Postman测试后端API,或者在前端进行上传测试。</think>### Vue与Python实现文件上传文件内容处理的完整方案 #### 一、前端实现(Vue) 1. **文件上传表单组件** ```vue <template> <div> <input type="file" @change="handleFileSelect" ref="fileInput"> <button @click="uploadFile">上传文件</button> <div v-if="progress > 0">上传进度: {{ progress }}%</div> </div> </template> <script> import axios from 'axios'; export default { data() { return { selectedFile: null, progress: 0 }; }, methods: { handleFileSelect(event) { this.selectedFile = event.target.files[0]; }, async uploadFile() { if (!this.selectedFile) return; const formData = new FormData(); formData.append('file', this.selectedFile); try { const response = await axios.post('http://localhost:8000/api/upload/', formData, { headers: { 'Content-Type': 'multipart/form-data' }, onUploadProgress: (progressEvent) => { this.progress = Math.round((progressEvent.loaded / progressEvent.total) * 100); } }); alert('上传成功: ' + response.data.message); } catch (error) { alert('上传失败: ' + error.message); } } } }; </script> ``` #### 二、后端实现(Django) 1. **配置Django路由与视图** ```python # urls.py from django.urls import path from .views import FileUploadView urlpatterns = [ path('api/upload/', FileUploadView.as_view(), name='file-upload'), ] ``` 2. **文件处理视图** ```python # views.py from django.http import JsonResponse from django.views import View import zipfile import os class FileUploadView(View): def post(self, request): uploaded_file = request.FILES.get('file') if not uploaded_file: return JsonResponse({'error': '未选择文件'}, status=400) # 保存原始文件 save_path = os.path.join('uploads', uploaded_file.name) with open(save_path, 'wb+') as destination: for chunk in uploaded_file.chunks(): destination.write(chunk) # 读取文件内容(示例:处理ZIP文件) if uploaded_file.name.endswith('.zip'): try: with zipfile.ZipFile(save_path, 'r') as zip_ref: # 提取所有文件到指定目录 zip_ref.extractall(os.path.join('uploads', 'extracted')) # 读取第一个文件内容 first_file = zip_ref.namelist()[0] with zip_ref.open(first_file) as f: content = f.read().decode('utf-8') # 存储到数据库(示例) # FileContent.objects.create(name=first_file, content=content) except zipfile.BadZipFile: return JsonResponse({'error': '无效的ZIP文件'}, status=400) return JsonResponse({'message': f'文件已保存,类型: {uploaded_file.content_type}'}) ``` #### 三、文件内容存储 1. **数据库模型设计(Django)** ```python # models.py from django.db import models class FileContent(models.Model): name = models.CharField(max_length=255) content = models.TextField() uploaded_at = models.DateTimeField(auto_now_add=True) ``` 2. **存储逻辑扩展** - 文本文件:直接读取内容并存入数据库 - CSV/Excel:使用`pandas`解析后存储结构化数据 - 图片/二进制文件:保存文件路径到数据库 #### 四、安全增强措施 1. 文件类型验证 ```python # views.py中添加 ALLOWED_EXTENSIONS = {'txt', 'zip', 'csv', 'png'} def is_allowed_file(filename): return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS # 在post方法中增加验证 if not is_allowed_file(uploaded_file.name): return JsonResponse({'error': '不支持的文件类型'}, status=400) ``` #### 五、部署注意事项 1. 跨域配置(Django) ```python # settings.py CORS_ALLOWED_ORIGINS = [ "http://localhost:8080", "http://127.0.0.1:8080" ] ``` 2. 文件存储路径配置 ```python # settings.py MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads') MEDIA_URL = '/media/' ``` #### 六、测试方案 1. 前端测试:上传不同文件类型(1MB文本文件、50MB ZIP文件) 2. 后端验证: - 检查文件完整性(MD5校验) - 压力测试(使用Locust进行并发上传测试)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值