vue实现文件预览和文件上传、下载、预览——多图、模型、dwg图纸、文档(word、excel、ppt、pdf)

news2024/11/27 7:34:29

整体思路(模型特殊不考虑,别人封装不具备参考性)

  1. 图片上传采用单独的组件,其他三种类型采用一个上传组件(仅仅文件格式不同)
  2. 文件上传采用前端直接上传阿里云的方式
  3. 图片预览使用elementUI自带的image预览
  4. dwg预览采用 kkfileview 方式,需要后台部署,前端使用 npm install js-base64 ,应该支持所有文件的预览,我们这里仅用作CAD文件的预览
  5. 文档类型的直接在浏览器打开预览,如果不支持需要拼接前缀 "https://view.officeapps.live.com/op/view.aspx?src="
  6. 图片下载为压缩包
  7. 文档类的相同方法下载

上传

在这里插入图片描述

        <el-form
          style="width: 650px"
          :model="form"
          :rules="rules"
          ref="form"
          label-width="180px"
          class="demo-form"
          :disabled="type == 3"
        >
          <el-form-item label="样板格式" prop="modelType">
            <el-radio-group v-model="form.modelType">
              <el-radio label="1">图片样板</el-radio>
              <el-radio label="2">三维样板</el-radio>
              <el-radio label="3">图纸样板</el-radio>
              <el-radio label="4">文档样板</el-radio>
            </el-radio-group>
          </el-form-item>
         <!-- 1 图片样板 2 三维样板 3 图纸样板 4 文档样板 -->
         <el-form-item
            v-if="form.modelType == 1"
            label="样板文件上传"
            prop="url"
         >
            <ImageUpload
              v-model="form.url"
              tip="请上传图片:"
              :limit="10"
              :showIcon="type == 3 ? true : false"
            ></ImageUpload>
         </el-form-item>
         <el-form-item
            v-if="form.modelType != 1"
            label="样板文件上传"
            prop="url"
         >
            <ModelUpload
              v-if="type != 3"
              v-model="form.url"
              :limit="1"
              :fileSize="
                form.modelType == 3 ? 50 : form.modelType == 4 ? 10 : 5120
              "
              :isShowUploadModel="true"
              ref="videoUploadRef"
              @input="uploadTemplate"
              :fileType="
                form.modelType == 3
                  ? ['.dwg']
                  : form.modelType == 4
                  ? ['.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.pdf']
                  : ['.rvt', '.ifc', '.obj', '.stl', '.fbx', '.3DS']
              "
            ></ModelUpload>
            <div v-else class="blue point" @click="preview(form)">
              {{ form.url?.split("/")[form.url?.split("/").length - 1] }}
            </div>
        </el-form-item>

ModelUpload.vue

<template>
  <div class="component-upload-image">
    <el-upload
      action=""
      :http-request="beforeUpload"
      class="avatar-uploader"
      :limit="limit"
      :on-remove="handleDelete"
      :on-error="handleUploadError"
      :on-exceed="handleExceed"
      name="file"
      :show-file-list="true"
      :file-list="fileList"
      ref="uploadRef"
      :on-preview="handlePreview"
      :data="otherQuery"
    >
      <el-button
        size="small"
        type="primary"
        v-if="!modelFlag"
        :disabled="disabled"
        >点击上传</el-button
      >
      <!-- 上传提示 -->
      <div class="el-upload__tip" slot="tip" v-if="showTip">
        <template v-if="fileSize">
          大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
        </template>
        <template v-if="fileType">
          文件类型支持
          <b style="color: #f56c6c">{{ fileType.join(" / ") }}</b> 格式
        </template>
      </div>
      <el-progress
        v-if="modelFlag == true"
        type="circle"
        :percentage="modelUploadPercent"
        style="margin-top: 7px"
      ></el-progress>
    </el-upload>
  </div>
</template>

<script>
export default {
  props: {
    value: [String, Object, Array],
    // 图片数量限制
    limit: {
      type: Number,
      default: 1,
    },
    // 大小限制(MB)
    fileSize: {
      type: Number,
      default: 5120,
    },
    fileType: {
      type: Array,
      default: () => [".rvt", ".ifc", ".dwg", ".obj", ".stl", ".fbx", ".3DS"],
    },
    // 是否显示提示
    isShowTip: {
      type: Boolean,
      default: true,
    },
    // 是否显示进度条
    isShowUploadModel: {
      type: Boolean,
      default: false,
    },
    // 是否显示重新上传按钮
    isShowBtn: {
      type: Boolean,
      default: true,
    },
    // 是否禁用上传按钮
    disabled: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      number: 0,
      hideUpload: false,
      fileList: [],
      otherQuery: {}, //上传文件传递额外参数
      uploadList: [],
      modelFlag: false,
      modelUploadPercent: 0,
      isCancel: false,
    };
  },
  watch: {
    value: {
      handler(val) {
        if (val) {
          console.log(val, "val");
          if (typeof val == "string") {
            // 首先将值转为数组
            const list = Array.isArray(val) ? val : this.value.split(",");
            // 然后将数组转为对象数组
            this.fileList = list.map((item) => {
              if (typeof item === "string") {
                item = {
                  name: item.substring(item.lastIndexOf("/") + 14),
                  url: item,
                };
              }
              return item;
            });
          } else {
            val.forEach((el) => {
              el.name = el.fileName;
            });
            this.fileList = val;
          }
        } else {
          this.fileList = [];
          return [];
        }
      },
      deep: true,
      immediate: true,
    },
  },
  computed: {
    // 是否显示提示
    showTip() {
      return this.isShowTip && (this.fileType || this.fileSize);
    },
  },
  methods: {
    //自定义上传方法..
    Upload(file, data) {
      console.log(file);
      let OSS = require("ali-oss");
      let client = new OSS({
        region: data.region,
        accessKeyId: data.accessKeyId,
        accessKeySecret: data.accessKeySecret,
        // accessKeyId: "",
        // accessKeySecret: "",
        bucket: "cscec83-openfile",
      });
      // let cdnUrl = data.cdnUrl;
      let cdnUrl = "https://cscec83-openfile.oss-cn-shanghai.aliyuncs.com/";
      this.number++;
      this.isCancel = false;
      const progress = (p, _checkpoint) => {
        console.log(p);
        // console.log(_checkpoint);
        this.modelFlag = true;
        this.modelUploadPercent = Number((Number(p) * 100).toFixed(1));
        console.log(this.isCancel);
        if (this.isCancel) {
          client.cancel();
        }
      };
      let fileName = "model/" + new Date().getTime() + file.file.name;
      client
        .multipartUpload(fileName, file.file, {
          progress,
          // 设置并发上传的分片数量。
          // parallel: 4,
          // 设置分片大小。默认值为1 MB,最小值为100 KB。
          partSize: 5 * 1024 * 1024,
        })
        .then((res) => {
          // console.log(res, "res");
          this.modelFlag = false;
          if (res.name) {
            let obj = {
              fileName: res.name,
              name: res.name,
              size: this.otherQuery.size,
              url: cdnUrl + res.name,
            };
            console.log(cdnUrl + res.name);
            this.uploadList.push(obj);
            if (this.uploadList.length === this.number) {
              this.fileList = this.fileList.concat(this.uploadList);
              this.uploadList = [];
              this.number = 0;
              // let list = this.fileList.map((item) => {
              //   return {
              //     ...item,
              //     modelUrl: item.url,
              //   };
              // });
              this.$emit("input", this.fileList);
            }
          } else {
            this.$modal.msgError("上传失败,请重试");
            this.cancel();
          }
        })
        .catch((err) => {
          console.log(err);
          if (err.name == "cancel") {
            this.$message("上传取消");
          } else {
            this.$modal.msgError(err);
          }
          this.cancel();
        });
    },
    handleDelete(file) {
      const findex = this.fileList
        .map((f) => f.fileName)
        .indexOf(file.fileName);
      if (findex > -1) {
        this.fileList.splice(findex, 1);
        this.$emit("input", this.fileList);
      }
      this.cancel();
    },
    // 上传前loading加载
    beforeUpload(file) {
      console.log(this.fileType, file.file.name);
      this.otherQuery = {};
      this.isCancel = false;
      var fileSize = file.file.size / 1024 / 1024 < this.fileSize; //控制大小  修改50的值即可
      let fileType = file.file.name.substring(file.file.name.lastIndexOf("."));
      let isContinue = true;
      if (
        this.fileType.indexOf(fileType) == -1 //控制格式
      ) {
        this.$modal.msgError(
          `文件格式不正确, 请上传${this.fileType.join("/")}格式文件!`
        );
        this.fileList = [];
        isContinue = false;
      }
      if (!fileSize) {
        this.$modal.msgError(`上传视频大小不能超过 ${this.fileSize} MB!`);
        this.fileList = [];
        isContinue = false;
      }
      this.otherQuery.fileName = file.file.name;
      this.otherQuery.size = (file.file.size / 1024 / 1024).toFixed(2);
      if (!isContinue) return;
      this.$axios.get("/file/ossFile/getOssParameter").then((res) => {
        if (res.code == 200) {
          this.Upload(file, res.data);
        }
      });
    },
    // 预览
    handlePreview(file) {
      window.open(file.url, "_blank");
    },
    // 文件个数超出
    handleExceed() {
      this.$message.error(`上传文件数量不能超过 ${this.limit} 个!`);
    },
    // 上传失败
    handleUploadError() {
      this.$modal.msgError("上传失败,请重试");
      // this.loading.close();
    },
    cancel(type) {
      this.isCancel = true;
      this.modelFlag = false;
    },
  },
};
</script>
<style scoped lang="css">
::v-deep.hideUpload .el-upload--picture-card {
  display: none;
}

::v-deep .el-upload--picture-card {
  width: 104px;
  height: 104px;
  line-height: 104px;
}

::v-deep .el-upload-list--picture-card .el-upload-list__item {
  width: 104px;
  height: 104px;
}

.avatar-uploader-icon {
  border: 1px dashed #d9d9d9 !important;
}

.avatar-uploader .el-upload {
  border: 1px dashed #d9d9d9 !important;
  border-radius: 6px !important;
  position: relative !important;
  overflow: hidden !important;
}

.avatar-uploader .el-upload:hover {
  border: 1px dashed #d9d9d9 !important;
  border-color: #409eff;
}

.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 300px;
  height: 178px;
  line-height: 178px;
  text-align: center;
}

.avatar {
  width: 300px;
  height: 178px;
  display: block;
}
</style>

ImageUpload.vue

<template>
  <div class="component-upload-image">
    <el-upload
      multiple
      :action="uploadImgUrl"
      list-type="picture-card"
      :on-success="handleUploadSuccess"
      :before-upload="handleBeforeUpload"
      :limit="limit"
      :on-error="handleUploadError"
      :on-exceed="handleExceed"
      name="file"
      :disabled="showIcon"
      :on-remove="handleRemove"
      :show-file-list="true"
      :headers="headers"
      :file-list="fileList"
      :on-preview="handlePictureCardPreview"
      :class="{
        hideUpload: this.fileList.length >= this.limit || this.showIcon,
      }"
    >
      <i class="el-icon-plus"></i>
    </el-upload>

    <!-- 上传提示 -->
    <div class="el-upload__tip" slot="tip" v-if="showTip">
      {{ tip }}
      <template v-if="fileSize">
        大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
      </template>
      <template v-if="fileType">
        格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
      </template>
    </div>

    <el-dialog
      :visible.sync="dialogVisible"
      title="预览"
      width="800"
      append-to-body
    >
      <img
        :src="dialogImageUrl"
        style="display: block; max-width: 100%; margin: 0 auto"
      />
    </el-dialog>
  </div>
</template>

<script>
export default {
  props: {
    value: [String, Object, Array],
    // 图片数量限制
    limit: {
      type: Number,
      default: 5,
    },
    // 大小限制(MB)
    fileSize: {
      type: Number,
      default: 10,
    },
    // 文件类型, 例如['png', 'jpg', 'jpeg']
    fileType: {
      type: Array,
      default: () => ["png", "jpg", "jpeg"],
    },
    tip: {
      type: String,
    },
    // 是否显示提示
    isShowTip: {
      type: Boolean,
      default: true,
    },
    // 是否禁用添加图片按钮
    showIcon: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      number: 0,
      uploadList: [],
      dialogImageUrl: "",
      dialogVisible: false,
      hideUpload: false,
      uploadImgUrl: "/prod-api" + "/file/upload", // 上传的图片服务器地址
      headers: {
        Authorization: this.$store.state.token,
      },
      fileList: [],
    };
  },
  watch: {
    value: {
      handler(val) {
        if (val) {
          // 首先将值转为数组
          const list = Array.isArray(val) ? val : this.value.split(",");
          // 然后将数组转为对象数组
          this.fileList = list.map((item) => {
            if (typeof item === "string") {
              item = { name: item, url: item };
            }
            return item;
          });
        } else {
          this.fileList = [];
          return [];
        }
      },
      deep: true,
      immediate: true,
    },
  },
  computed: {
    // 是否显示提示
    showTip() {
      return this.isShowTip && (this.fileType || this.fileSize);
    },
  },
  methods: {
    // 删除图片
    handleRemove(file, fileList) {
      const findex = this.fileList.map((f) => f.name).indexOf(file.name);
      if (findex > -1) {
        this.fileList.splice(findex, 1);
        this.$emit("input", this.listToString(this.fileList));
      }
    },
    // 上传成功回调
    handleUploadSuccess(res) {
      this.uploadList.push({ name: res.data.url, url: res.data.url });
      if (this.uploadList.length === this.number) {
        this.fileList = this.fileList.concat(this.uploadList);
        this.uploadList = [];
        this.number = 0;
        this.$emit("input", this.listToString(this.fileList));
        this.$modal.closeLoading();
      }
    },
    // 上传前loading加载
    handleBeforeUpload(file) {
      let isImg = false;
      if (this.fileType.length) {
        let fileExtension = "";
        if (file.name.lastIndexOf(".") > -1) {
          fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
        }
        isImg = this.fileType.some((type) => {
          if (file.type.indexOf(type) > -1) return true;
          if (fileExtension && fileExtension.indexOf(type) > -1) return true;
          return false;
        });
      } else {
        isImg = file.type.indexOf("image") > -1;
      }

      if (!isImg) {
        this.$modal.msgError(
          `文件格式不正确, 请上传${this.fileType.join("/")}图片格式文件!`
        );
        return false;
      }
      if (this.fileSize) {
        const isLt = file.size / 1024 / 1024 < this.fileSize;
        if (!isLt) {
          this.$modal.msgError(`上传头像图片大小不能超过 ${this.fileSize} MB!`);
          return false;
        }
      }
      this.$modal.loading("正在上传图片,请稍候...");
      this.number++;
    },
    // 文件个数超出
    handleExceed() {
      this.$modal.msgError(`上传文件数量不能超过 ${this.limit} 个!`);
    },
    // 上传失败
    handleUploadError() {
      this.$modal.msgError("上传图片失败,请重试");
      this.$modal.closeLoading();
    },
    // 预览
    handlePictureCardPreview(file) {
      this.dialogImageUrl = file.url;
      this.dialogVisible = true;
    },
    // 对象转成指定字符串分隔
    listToString(list, separator) {
      let strs = "";
      separator = separator || ",";
      for (let i in list) {
        strs += list[i].url + separator;
      }
      return strs != "" ? strs.substr(0, strs.length - 1) : "";
    },
  },
};
</script>
<style scoped >
::v-deep.hideUpload .el-upload--picture-card {
  display: none;
}

::v-deep .el-upload--picture-card {
  width: 86px;
  height: 86px;
  line-height: 86px;
}

::v-deep .el-upload-list--picture-card .el-upload-list__item {
  width: 86px;
  height: 86px;
}

::v-deep .el-list-enter-active,
::v-deep .el-list-leave-active {
  transition: all 0s;
}

::v-deep .el-list-enter,
.el-list-leave-active {
  opacity: 0;
  transform: translateY(0);
}
</style>

预览和下载

在这里插入图片描述

<template>
  <div id="index">
      <el-card shadow="always" style="margin-top: 20px">
        <div class="model pd-16">
          <div class="flex mb-20">
            <div class="title-left bold size-22">工程样板</div>
            <div
              class="flex_r point border-blue blue plr-10 ptb-4 bg-white radius-4 size-14"
              @click="toTemplate"
            >
              查看更多 <i class="el-icon-right"></i>
            </div>
          </div>
          <div class="flex_l">
            <div
              class="center mr-20 plr-20 ptb-10"
              @click="changeTab(1)"
              :style="
                tab == 1
                  ? 'background: linear-gradient(210.65deg, #5FA4FE 0%, #367EF8 100%);border-radius: 25px;color:#fff'
                  : ''
              "
            >
              <img
                :src="`${
                  tab == 1
                    ? require('../../assets/img/zhi-tab-1-1.png')
                    : require('../../assets/img/zhi-tab-1.png')
                }`"
                alt=""
                style="width: 27px"
              />
              <div class="ml-16 size-18">图片区</div>
            </div>
            <div
              class="center mr-20 plr-20 ptb-10"
              @click="changeTab(2)"
              :style="
                tab == 2
                  ? 'background: linear-gradient(210.65deg, #5FA4FE 0%, #367EF8 100%);border-radius: 25px;color:#fff'
                  : ''
              "
            >
              <img
                :src="`${
                  tab == 2
                    ? require('../../assets/img/zhi-tab-4-1.png')
                    : require('../../assets/img/zhi-tab-4.png')
                }`"
                alt=""
                style="width: 27px"
              />
              <div class="ml-16 size-18">三维模型区</div>
            </div>
            <div
              class="center mr-20 plr-20 ptb-10"
              @click="changeTab(3)"
              :style="
                tab == 3
                  ? 'background: linear-gradient(210.65deg, #5FA4FE 0%, #367EF8 100%);border-radius: 25px;color:#fff'
                  : ''
              "
            >
              <img
                :src="`${
                  tab == 3
                    ? require('../../assets/img/zhi-tab-3-1.png')
                    : require('../../assets/img/zhi-tab-3.png')
                }`"
                alt=""
                style="width: 27px"
              />
              <div class="ml-16 size-18">图纸区</div>
            </div>

            <div
              class="center mr-20 plr-20 ptb-10"
              @click="changeTab(4)"
              :style="
                tab == 4
                  ? 'background: linear-gradient(210.65deg, #5FA4FE 0%, #367EF8 100%);border-radius: 25px;color:#fff'
                  : ''
              "
            >
              <img
                :src="`${
                  tab == 4
                    ? require('../../assets/img/zhi-tab-2-1.png')
                    : require('../../assets/img/zhi-tab-2.png')
                }`"
                alt=""
                style="width: 27px"
              />
              <div class="ml-16 size-18">文档区</div>
            </div>
          </div>
          <el-row :gutter="20" class="mt-30">
            <div v-if="templateList.length == 0" class="text-center">
              <img src="../../assets/img/null.png" alt="" style="width: 4rem" />
              <div>暂无数据</div>
            </div>
            <el-col :span="6" v-for="(item, i) in templateList" :key="i + 'c'">
              <el-card shadow="hover" class="point course mb-12">
                <el-image
                  v-if="tab == 1"
                  style="width: 100%; height: 170px; border-radius: 5px"
                  mode="aspectFill"
                  :src="item.pictureUrl"
                  :preview-src-list="[item.url.split(',')]"
                >
                </el-image>
                <img
                  @click="preview(item)"
                  v-if="tab == 2 || tab == 3 || tab == 4"
                  :src="
                    tab == 4
                      ? require(`../../assets/img/doc-${
                          item.docType || 'word'
                        }.png`)
                      : item.pictureUrl
                  "
                  style="width: 100%; height: 170px; border-radius: 5px"
                  mode="aspectFill"
                />
                <div class="flex mtb-10 mlr-12">
                  <div class="line-1 mr-10">{{ item.modelName }}</div>
                  <img
                    style="width: 22px"
                    src="../../assets/img/download.png"
                    alt=""
                    @click.stop="download(item)"
                  />
                </div>
              </el-card>
            </el-col>
          </el-row>
        </div>
      </el-card>

      <modelPop ref="modelPopRef"></modelPop>
    </div>
  </div>
</template>

<script>
// npm install js-base64
import { Base64 } from "js-base64";
export default {
  // layout: "default-all",
  data() {
    return {
      tab: 1,
      templateList: [],
    };
  },
  mounted() { 
    this.getTemplateList();
  },
  methods: {
    changeTab(tab) {
      this.tab = tab;
      this.getTemplateList();
    },
    // 下载
    download(item) {
      // 1 图片样板 2 三维样板 3 图纸样板 4 文档样板
      if (this.tab == 1) {
        this.$downloadZip.zip(
          "/qualityTrain/sample/downloadAndZip?sampleId=" + item.sampleId,
          item.modelName + "样板文件.zip"
        );
      } else if (this.tab == 2 || this.tab == 3) {
        window.open(item.url);
      } else if (this.tab == 4) {
        fetch(item.url)
          .then((res) => res.blob())
          .then((blob) => {
            const a = document.createElement("a");
            const objectUrl = window.URL.createObjectURL(blob);
            a.download =
              item.modelName +
              (item.docType == "ppt"
                ? ".ppt"
                : item.docType == "excel"
                ? ".xls"
                : item.docType == "word"
                ? ".doc"
                : ".pdf");
            a.href = objectUrl;
            a.click();
          });
      }
    },
     
    getTemplateList() {
      this.$axios
        .get("/qualityTrain/sample/homePage", {
          params: {
            modelType: this.tab,
          },
        })
        .then((res) => {
          this.templateList = res.data;
        });
    },
    // 预览
    preview(item) {
      // 1 图片样板 2 三维样板 3 图纸样板 4 文档样板
      if (this.tab == 2) {
        this.$refs.modelPopRef.init(item.sampleId, item.modelName, false, true);
      } else if (this.tab == 3) {
        let base = location.protocol + "//" + location.hostname + ":8012";
        let url =
          (base.includes("192.168.2.89") ? "http://192.168.0.19:8012" : base) +
          "/onlinePreview?url=" +
          encodeURIComponent(Base64.encode(item.url));
        window.open(url, "_blank");
      } else {
        console.log(item.docType);
        window.open(
          (item.docType != "pdf"
            ? "https://view.officeapps.live.com/op/view.aspx?src="
            : "") + item.url
        );
      }
    },
  },
};
</script> 

downloadZip.js

import axios from 'axios'
import { saveAs } from 'file-saver'
import { Loading, Message } from 'element-ui';
export default ({ store }, inject) => {
  const zip = (url, name) => {
    let loadingInstance = Loading.service({
      lock: true,
      text: '正在下载...',
      spinner: 'el-icon-loading',
      background: 'rgba(0, 0, 0, 0.7)'
    });
    const fullUrl = '/prod-api' + url;
    axios({
      method: 'get',
      url: fullUrl,
      responseType: 'blob',
      timeout: 600000,
      headers: { 'Authorization': store.state.token }
    }).then(async (res) => {
      const isLogin = await blobValidate(res.data);
      loadingInstance.close();
      if (isLogin) {
        const blob = new Blob([res.data], { type: 'application/zip' })
        saveAs(blob, name)
      } else {
        printErrMsg(res.data);
      }
    }).catch(() => {
      loadingInstance.close();
    });
  };
  // 验证是否为blob格式
  async function blobValidate(data) {
    try {
      const text = await data.text();
      JSON.parse(text);
      return false;
    } catch (error) {
      return true;
    }
  }
  let errorCode = {
    '401': '认证失败,无法访问系统资源',
    '403': '当前操作没有权限',
    '404': '访问资源不存在',
    'default': '系统未知错误,请反馈给管理员'
  }

  async function printErrMsg(data) {
    const resText = await data.text();
    const rspObj = JSON.parse(resText);
    const errMsg = errorCode[rspObj.code] || rspObj.msg || errorCode['default']
    Message.error(errMsg);
  }
  // 注入到全局上下文中
  inject('downloadZip', {
    zip,
  });
}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2219446.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

双目标定的原理

标定目的&#xff1a;建立相机成像几何模型并矫正透镜畸变。 建立相机成像几何模型&#xff1a;计算机视觉的首要任务就是要通过拍摄到的图像信息获取到物体在真实三维世界里相对应的信息&#xff0c;于是&#xff0c;建立物体从三维世界映射到相机成像平面这一过程中的几何模…

ssm剧本杀预约系统+vue

系统包含&#xff1a;源码论文 所用技术&#xff1a;SpringBootVueSSMMybatisMysql 免费提供给大家参考或者学习&#xff0c;获取源码请私聊我 需要定制请私聊 目 录 第1章 绪论 1 1.1 课题背景 1 1.2 课题意义 1 1.3 研究内容 2 第2章 开发环境与技术 3 2.1 Java语言…

【p2p、分布式,区块链笔记 Blockchain】truffle002 unleashed_rentable_nft 项目

上一篇&#xff1a;【p2p、分布式&#xff0c;区块链笔记 Blockchain】truffle001 以太坊开发框架truffle初步实践 项目结构 项目实现了一个简单的可租赁的 NFT 系统&#xff0c;用户可以铸造和销毁 NFT。这是作者写的项目介绍&#xff08;后边看issue才发现的&#xff09;&a…

xtrabackup工具介绍、安装及模拟数据库故障使用xtrabackup工具恢复数据等操作详细说明

一、xtrabackup工具介绍 Percona XtraBackup Percona XtraBackup是一个适用于MySQL的开源热备份工具&#xff0c;它在备份期间不锁表。它可以备份InnoDB、XtraDB以及MyISAM存储引擎的表。 2.4版本支持MySQL5.1、5.5、5.6以及5.7。 它有两个实用命令&#xff0c;分别是xtraback…

LeetCode_2469. 温度转换_java

1、问题 2469. 温度转换. - 备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/convert-the-temperature/description/ 给你一个四舍五入到两位小数的非负浮点数 celsiu…

day3:管道,解压缩,vim

一&#xff0c;管道&#xff08;|&#xff09; 引入 当我们要将本次命令结果作为下次命令参数时就可以用到&#xff0c;极大的简化了操作。 比如&#xff1a;head -5 文件| tail -1&#xff1a;表示显示第五行这就是管道的魅力 概述 管道符&#xff1a;| 作用&#xff1a…

计算机网络——可靠数据传输原理

变成什么状态取决于当时发生的事件 在停止等待协议中只用1位来表示序号0和1——新的和旧的 在这里插入图片描述

IT运维的365天--017 如何在两台Linux服务器之间快速传输文件夹(同时设置免密)

前情提要(两台Linux服务器之间传输批量文件夹): 两台都是外网服务器,都是Linux系统(CentOS),都安装了宝塔,用于搭建巨量的静态网站,由于A服务器准备不要了,所以要在A服务器转移几百个静态网站到B服务器。 Linux下scp单命令传输文件夹测试: 准备工作,先测试转移一…

【Kafka】Kafka Producer的缓冲池机制原理

如何初始化的bufferPool的 在初始化的时候 初始化BufferPool对象 // 设置缓冲区 this.accumulator new RecordAccumulator(xxxxx,其他参数,new BufferPool(this.totalMemorySize, config.getInt(ProducerConfig.BATCH_SIZE_CONFIG), metrics, time, PRODUCER_METRIC_GROUP_N…

免费送源码:Java+SpringBoot+MySQL SpringBoot珠宝店客户关系管理系统 计算机毕业设计原创定制

摘 要 随着计算机技术的发展&#xff0c;特别是计算机网络技术与数据库技术的发展&#xff0c;使用人们的生活与工作方式发生了很大的改观。本课题研究的珠宝店客户关系管理系统&#xff0c;主要功能模块包括系统用户&#xff0c;部门类别&#xff0c;职务类别&#xff0c;外出…

【ssh】Mac 使用 ssh 连接阿里云报错:Connection reset by 8.155.1.xxx port 22

Mac 使用 ssh 连接阿里云报错&#xff1a;Connection reset by 8.155.1.xxx port 22 问题描述解决办法 问题描述 Connection reset by 8.155.1.xxx port 22解决办法 关掉代理 VPN

SpringCloudAlibaba升级手册

目录 1. 版本对照 版本现状 SpringCloud与AlibabaCloud对应版本 Springboot与Elasticsearch版本对应 2. openfeign问题 问题 解决方案 3. Feign请求问题 问题 解决方法 4. Sentinel循环依赖 问题 解决方案 5. bootstrap配置文件不生效 问题 解决方案 6. Nacos的…

visio导出pdf公式变形问题杂谈

其实不会变形。 我自己的情况是直接用edge PDF阅读器打开pdf看到的是公式有变形&#xff08;常见是字体、形状变了&#xff09;&#xff0c;但换一个pdf阅读器如adobe的就是正常的了 不过大家一般是用edge pdf阅读器直接打开查看&#xff0c;所以通过visio打印的方式导出pdf可…

DNS 与 ICMP

DNS(Domain Name System)快速了解 DNS 是一整套从域名映射到 IP 的系统 DNS 背景 TCP/IP 中使用 IP 地址和端口号来确定网络上的一台主机的一个程序. 但是 IP 地址不方便记忆 于是人们发明了一种叫主机名的东西, 是一个字符串, 并且使用 hosts 文件来描述主机 名和 IP 地…

【Hive】8-Hive性能优化及Hive3新特性

Hive性能优化及Hive3新特性 Hive表设计优化 Hive查询基本原理 Hive的设计思想是通过元数据解析描述将HDFS上的文件映射成表 基本的查询原理是当用户通过HQL语句对Hive中的表进行复杂数据处理和计算时&#xff0c;默认将其转换为分布式计算 MapReduce程序对HDFS中的数据进行…

基于排名的股票预测的关系时态图卷积网络(RT-GCN)

“ 为了充分利用股票之间的关系&#xff0c;获得最高收益&#xff0c;提出了一种关系时态图卷积网络(RT-GCN)。” 标题&#xff1a;Relational Temporal Graph Convolutional Networks for Ranking-Based Stock Prediction 链接&#xff1a;https://ieeexplore.ieee.org/do…

Android15之解决gdb:Remote register badly formatted问题(二百三十六)

简介&#xff1a; CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a; 多媒体系统工程师系列【…

2024全国大数据与计算智能挑战赛火热报名中!

一年一度的 全国大数据与计算智能挑战赛震撼来袭&#xff01; 报名速通&#xff1a; https://www.datafountain.cn/special/BDSSF2024 大数据与决策&#xff08;国家级&#xff09;实验室连续三年组织发起全国大数据与计算智能挑战赛&#xff0c;旨在深入挖掘大数据应用实践中亟…

STM32传感器模块编程实践(九) VL53L0X激光红外测距传感器简介及驱动源码

文章目录 一.概要二.VL53L0X测距原理三.VL53L0X主要特性四.VL53L0X硬件参考设计五.模块接线说明六.模块通讯协议介绍七.光学盖玻片介绍八.STM32单片机与VL53L0模块实现距离测量实验1.硬件准备2.软件工程3.软件主要代码4.实验效果 九.小结 一.概要 VL53L0X是一款由ST&#xff0…

通过PHP与API的结合,开启电商数据集成的新篇章

在数字化转型的浪潮中&#xff0c;电子商务数据的集成对于企业来说变得越来越重要。无论是在线零售商还是品牌商&#xff0c;都需要实时访问商品数据以优化库存管理、制定定价策略、提升客户体验。PHP&#xff0c;作为服务端脚本语言的佼佼者&#xff0c;为开发者提供了强大的工…