记录一次vue项目页面内嵌iframe页面实现跨域上传和下载附件的功能

功能背景:项目部署在外网,然后其中有一个功能需要上传下载附件,附件是上传到华为云对象存储服务OBS中(私有云),所以采用iframe嵌套页面的方式解决跨域问题。
实现思路:
1、父窗口封装一个组件专门用于处理iframe相关功能
2、子窗口是另外一个项目,封装了附件上传、下载、删除等功能
3、父窗口通过监听组件绑定的value值,不断轮询去问子窗口是否已经挂载完毕了,如果已经挂载了,就将接口返回的附件列表发送给子窗口进行展示
4、子窗口挂载完毕后主动告知父窗口已经挂载好了,可以接收消息了。子窗口接收到了附件列表后进行展示,上传成功、删除成功后子窗口都需要发送消息给父窗口,告知附件列表发生了改变
5、父窗口是vue2语法写的,子窗口是vue3写的,可自行更改。

父窗口(vue2语法)

<template><div><iframe id="modle_iframe" ref="modle_iframe" frameborder="no" border="0"style="width:100%;height:calc(100vh - 450px);" :src="`https://xxxxx:8443/rcxjsp`"></iframe></div>
</template><script>
/*** @Date 2025-05-14* @Description 视频上传组件*/
export default {props: {value: {     //附件列表type: Array,default: () => { return [] }},disabled: {      //是否只读type: Boolean,default: false}},data() {return {fileList: [],isIframeLoaded: false,  //标记iframe是否加载完成timer: null}},watch: {value: {immediate: true,handler(newValue, oldValue) {if (newValue && newValue.length > 0) {this.fileList = newValue;} else {this.fileList = [];}this.loop();}},},mounted() {window.addEventListener('message', this.getMessage);},beforeDestroy() {window.removeEventListener('message', this.getMessage);if (this.timer) {clearInterval(this.timer);}},methods: {//不断询问子窗口是否已经挂载完毕,只有挂载完毕后发送消息,子窗口才能接收到loop() {if (this.timer) {clearInterval(this.timer);}this.timer = setInterval(() => {if (this.isIframeLoaded) {this.sendMessage();}}, 100)},sendMessage() {if (this.timer) {clearInterval(this.timer);}const iframe = document.getElementById('modle_iframe');const data = {disabled: this.disabled,fileList: this.fileList,}const msg = JSON.stringify(data);//父窗口发送列表和是否只读给子窗口iframe.contentWindow.postMessage(msg, '*');},getMessage(event) {    //父窗口接收来自子窗口的消息const data = JSON.parse(event.data);if (data.type === 'onMounted') {this.isIframeLoaded = true;} else if (data.type == 'updateFileList') {this.fileList = data.fileList;this.$emit('input', this.fileList);}},}
}
</script><style lang="scss" scoped></style>

子窗口(vue3+element-plus)

这里的上传和下载都是采用的分片上传和分片下载实现的。这里只提供核心代码,有用得上的自行修改。

<template><div style="overflow-y: auto;"><el-upload v-if="!disabled" action="#" :file-list="fileList" :data="{ path: 'rcxjsp' }" :show-file-list="false":http-request="uploadRequest"><el-button type="primary" :loading="loading" style="margin-bottom: 10px;">点击上传</el-button></el-upload><!-- 文件列表 --><div v-for="(item, index) in fileList" :key="index" class="fileList" style="display: flex;"><img src="./images/pdf.png" mode="" v-if="fileHz(item) == 'pdf'"> </img><img src="./images/zip.png" mode="" v-else-if="fileHz(item) == 'zip' || fileHz(item) == 'rar'"></img><img src="./images/txt.png" mode="" v-else-if="fileHz(item) == 'txt'"></img><img src="./images/ppt.png" mode="" v-else-if="fileHz(item) == 'ppt'"></img><img src="./images/xls.png" mode="" v-else-if="fileHz(item) == 'xls' || fileHz(item) == 'xlsx'"></img><img src="./images/doc.png" mode="" v-else-if="fileHz(item) == 'doc' || fileHz(item) == 'docx'"></img><img src="./images/mp3.png" mode=""v-else-if="fileHz(item) == 'mp3' || fileHz(item) == 'wav' || fileHz(item) == 'wma'"></img><img src="./images/mp4.png" mode=""v-else-if="fileHz(item) == 'mp4' || fileHz(item) == 'avi' || fileHz(item) == 'mov'"></img><img src="./images/fj.png" mode="" v-else></img><div class="filename">{{ item.fileName || item.name }}</div><el-button type="primary" @click="fpDown(item, index)" class="yulan":loading="downloading[index].loading">{{ downloading[index].loading ?`正在下载(${downloading[index].percent}%)` : '下载' }}</el-button><el-button type="danger" @click.stop="handleDelete(index)" v-show="!disabled">删除</el-button></div><el-progress v-show="showProgress" :percentage="progressPercent"></el-progress></div>
</template><script setup>
/*** @Date 2025-05-13* @Description 视频上传下载*/
import { initializeUploadId, uploadChunk, completeUpload, rangeDownload } from '@/api/rcxjsp';//父组件发送过来的两个参数
const fileList = ref([]);
const disabled = ref(false);
//本组件需要使用到的变量
let loading = ref(false);
let downloading = ref([]); // 下载进度
let currentFile = ref({}); // 当前文件
let showProgress = ref(false); // 进度条
let progressPercent = ref(0); // 进度百分比
const path = 'rcxjsp'; // 上传路径onMounted(() => {//页面挂载后主动通知父窗口已经准备好了window.parent.postMessage(JSON.stringify({ type: 'onMounted' }), '*');window.addEventListener('message', getMessage);
});
onUnmounted(() => {window.removeEventListener('message', getMessage);
});
const getMessage = (event) => {if (event.source === window.parent) {const data = JSON.parse(event.data);disabled.value = data.disabled;fileList.value = data.fileList;downloading.value = new Array(fileList.value.length).fill({ loading: false, percent: 0 });}
}
//获取文件后缀名
const fileHz = (file) => {const name = file.fileName || file.name;if (name) {var index = name.lastIndexOf(".");var ext = name.substr(index + 1);return ext}
}
//上传附件
const uploadRequest = (params) => {currentFile.value.fileName = params.file.name;showProgress.value = true;loading.value = true;let formdata = new FormData();formdata.append('file', params.file);formdata.append('path', path);formdata.append('fileName', params.file.name);initializeUploadId({ fileName: params.file.name, path: path }).then(res => {currentFile.value = res.data;const chunkSize = 1024 * 1024 * 25; // 每个切片的大小(这里设置为25MB)const totalChunks = Math.ceil(params.file.size / chunkSize);let currentChunk = 0;const uploadNextChunk = () => {const start = currentChunk * chunkSize;const end = Math.min((currentChunk + 1) * chunkSize, params.file.size);let chunkData = params.file.slice(start, end);let formdata = new FormData();formdata.append('file', chunkData);formdata.append('uploadId', currentFile.value.uploadId);formdata.append('objectKey', currentFile.value.objectKey);formdata.append('chunkIndex', currentChunk + 1);uploadChunk(formdata).then(res => {currentChunk++;progressPercent.value = +Math.floor((currentChunk / totalChunks) * 100).toFixed(0);if (currentChunk < totalChunks) {uploadNextChunk();} else {// 所有切片上传完成,将切片合并progressPercent.value = 100;complete();}}).catch((error) => {ElMessage({ type: 'error', message: '切片上传失败!' });loading.value = false;showProgress.value = false;progressPercent.value = 0;});};uploadNextChunk();})
}
//合并切片
const complete = () => {completeUpload({ ...currentFile.value }).then(res => {//定义需要传输给父窗口的附件列表数据结构let fileitem = {name: res.data.fileName,fileName: res.data.fileName,url: res.data.fileUrl,fileUrl: res.data.fileUrl,fileLength: res.data.fileLength}fileList.value.push(fileitem)downloading.value.push({ loading: false, percent: 0 });//通知父窗口附件列表变化了sendMsgToParent();}).catch(() => {ElMessage({ type: 'error', message: '切片合并失败!' });}).finally(() => {loading.value = false;showProgress.value = false;progressPercent.value = 0;})
}
//删除文件
const handleDelete = (index) => {fileList.value.splice(index, 1);//通知父窗口附件列表变化了sendMsgToParent();
}
//分片下载
const fpDown = (e, index) => {   //分片下载同步下载(按顺序一个一个下载)downloading.value.splice(index, 1, { loading: true, percent: 0 });const chunkSize = 1024 * 1024 * 25; // 每个切片的大小(这里设置为25MB)const totalChunks = Math.ceil(e.fileLength / chunkSize);let currentChunk = 0;const chunks = [];const download = () => {const start = currentChunk * chunkSize;const end = Math.min((currentChunk + 1) * chunkSize, e.fileLength);rangeDownload({ fileName: e.fileName, start: start, end: end, fileLength: e.fileLength }).then(bolb => {currentChunk++;let percent = +Math.floor((currentChunk / totalChunks) * 100).toFixed(0);downloading.value.splice(index, 1, { loading: true, percent: percent });chunks.push(bolb);if (currentChunk < totalChunks) {download();} else {downloading.value.splice(index, 1, { loading: false, percent: 100 });const mergedBlob = new Blob(chunks);const downloadUrl = window.URL.createObjectURL(mergedBlob);const link = document.createElement('a');link.href = downloadUrl;link.setAttribute('download', e.fileName);link.click();window.URL.revokeObjectURL(downloadUrl);}}).catch((error) => {ElMessage({ type: 'error', message: '切片下载失败!' });downloading.value.splice(index, 1, { loading: false, percent: 0 });});}download();
}//通知父窗口文件列表改变
const sendMsgToParent = () => {const msg = {type: 'updateFileList',fileList: fileList.value}window.parent.postMessage(JSON.stringify(msg), '*');
}</script><style lang="scss" scoped>
.fileList {display: flex;align-items: center;margin-bottom: 10px;img,.el-image {width: 30px;height: 30px;margin-right: 10px;}div.filename {flex: 1;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;}el-button.yulan {margin-left: 10px;}el-button.delete {margin-left: 10px;}
}
</style>

接口rcxjsp.js大致如下:

import request from '@/utils/request'//获取uploadid
export function initializeUploadId(params) {return request({url: '/xxxxx',method: 'get',params,})
}//上传分片
export function uploadChunk(data) {return request({headers: {'Content-Type': 'multipart/form-data',},url: '/gtfrgftft',method: 'post',data,})
}//合并分片
export function completeUpload(data) {return request({url: '/thgthtgh',method: 'post',data,})
}//下载附件
export function download(params) {return request({url: '/wrderfefre',method: 'get',params,responseType: 'blob',})
}//分片下载
export function rangeDownload(params) {return request({url: 'kuju',method: 'get',params,responseType: 'blob',})
}

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

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

相关文章

rust语言,与c,go语言一样也是编译成二进制文件吗?

是的&#xff0c;Rust 和 C、Go 一样&#xff0c;默认情况下会将代码编译成二进制可执行文件&#xff08;如 ELF、PE、Mach-O 等格式&#xff09;&#xff0c;但它们的编译过程和运行时特性有所不同&#xff1a; 1. Rust&#xff08;类似 C&#xff0c;直接编译为机器码&#x…

后端框架(3):Spring(2)

AOP 概述&#xff1a;AspectOrientedProgramming 面向切面编程&#xff1a;是对面向对象编程的补充延续&#xff0c;面向切面编程思想是将程序中非业务代码(提交事务&#xff0c;打印日志&#xff0c;权限验证&#xff0c;统一异常处理) 然后在调用业务代码时&#xff0c;通过…

Vue3中setup运行时机介绍

在 Vue3 中&#xff0c;直接写在 <script setup>...</script> 中的代码运行时机可以分为以下几个关键阶段&#xff1a; 一、执行顺序层级 #mermaid-svg-bF3p98MiNdLfcoSG {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#33…

Ubuntu快速安装Python3.11及多版本管理

之前文章和大家分享过&#xff0c;将会出一篇专栏&#xff08;从电脑装ubuntu系统&#xff0c;到安装ubuntu的常用基础软件&#xff1a;jdk、python、node、nginx、maven、supervisor、minio、docker、git、mysql、redis、postgresql、mq、ollama等&#xff09;&#xff0c;目前…

裸金属服务器和云服务器之间的差别

裸金属服务器能够直接在硬件上运行&#xff0c;不需要额外的虚化层&#xff0c;让每个应用程序或者是服务都能够在实际的硬件上运行&#xff0c;不需要和其他虚拟服务器来共享资源&#xff1b;而云服务器作为一种虚拟服务器&#xff0c;是通过虚拟化技术为企业提供一个独立的计…

C++ 中的几种锁机制整理

1. 互斥锁&#xff08;std::mutex&#xff09; ✅ 简介 最常用的线程同步工具。保证同一时间只能有一个线程访问临界区。 ✅ 使用方式 #include <mutex>std::mutex mtx;void safeFunction() {std::lock_guard<std::mutex> lock(mtx);// 临界区代码 }✅ 优点 简…

Graph Representation Learning【图最短路径优化/Node2vec/Deepwalk】

文章目录 Q1&#xff1a;网络性质&#xff1a;1.数据读取与邻接表构建&#xff1a;2.基本特征和连通性&#xff1a; 算法思路&#xff1a;1. 广度优先搜索&#xff08;BFS&#xff09;标记前驱:2. 回溯生成所有最短路径: 实验结果&#xff1a;复杂度分析&#xff1a; Q2&#x…

MATLAB中的概率分布生成:从理论到实践

MATLAB中的概率分布生成&#xff1a;从理论到实践 引言 MATLAB作为一款强大的科学计算软件&#xff0c;在统计分析、数据模拟和概率建模方面提供了丰富的功能。本文将介绍如何使用MATLAB生成各种常见的概率分布&#xff0c;包括均匀分布、正态分布、泊松分布等&#xff0c;并…

经典算法 (A/B) mod C

(A/B) mod C 问题描述 求(A/B)%C&#xff0c;但由于A和B实在太大了&#xff0c;我们只给出A % C&#xff0c;B % C。 (我们保证给定的A必能被B整除&#xff0c;且gcd(B,C) 1)。 输入描述 输入一行三个整数&#xff0c;分别是A % C&#xff0c;B % C&#xff0c;C。 输出…

大数据技术的主要方向及其应用详解

文章目录 一、大数据技术概述二、大数据存储与管理方向1. 分布式文件系统2. NoSQL数据库3. 数据仓库技术 三、大数据处理与分析方向1. 批处理技术2. 流处理技术3. 交互式分析4. 图计算技术 四、大数据机器学习方向1. 分布式机器学习2. 深度学习平台3. 自动机器学习(AutoML) 五、…

Deeper and Wider Siamese Networks for Real-Time Visual Tracking

现象&#xff1a; the backbone networks used in Siamese trackers are relatively shallow, such as AlexNet , which does not fully take advantage of the capability of modern deep neural networks. direct replacement of backbones with existing powerful archite…

ubuntu22.04卸载vscode

方法 1&#xff1a;通过 Snap 卸载 VSCode 如果你是通过 Snap 安装的 VSCode&#xff08;Ubuntu 22.04 默认推荐方式&#xff09;&#xff0c;按照以下步骤卸载&#xff1a; 检查是否通过 Snap 安装&#xff1a; bash snap list | grep code如果输出显示 code&#xff0c;说明…

OpenCV 背景建模详解:从原理到实战

在计算机视觉领域&#xff0c;背景建模是一项基础且重要的技术&#xff0c;它能够从视频流中分离出前景目标&#xff0c;广泛应用于运动目标检测、视频监控、人机交互等场景。OpenCV 作为计算机视觉领域最受欢迎的开源库之一&#xff0c;提供了多种高效的背景建模算法。本文将深…

Android native崩溃问题分析

最近在做NDK项目的时候&#xff0c;出现了启动应用就崩溃了&#xff0c;崩溃日志如下&#xff1a; 10:41:04.743 A Build fingerprint: samsung/g0qzcx/g0q:13/TP1A.220624.014/S9060ZCU4CWH1:user/release-keys 10:41:04.743 A Revision: 12 10:41:04.743 A ABI: arm64…

【Shell的基本操作】

文章目录 一、实验目的二、实验环境三、实验内容3.1 Shell变量与脚本基础3.2 定制终端提示符&#xff08;PS1变量&#xff09;3.3 文件查找与类型确认&#xff08;find命令&#xff09;3.4 管道命令实战&#xff08;用户登录统计&#xff09;3.5 交互式备份压缩脚本 四、总结4.…

快速选择算法:优化大数据中的 Top-K 问题

在处理海量数据时&#xff0c;经常会遇到这样的需求&#xff1a;找出数据中最大的前 K 个数&#xff0c;而不必对整个数据集进行排序。这种场景下&#xff0c;快速选择算法&#xff08;Quickselect&#xff09;就成了一个非常高效的解决方案。本文将通过一个 C 实现的快速选择算…

AQS 基本思想与源码分析

充分了解 AbstractQueuedSynchronizer 对于深入理解并发编程是有益处的&#xff0c;它是用来构建锁或者其他同步组件的基础框架&#xff0c;我们常用的同步工具类如 CountDownLatch、Semaphore、ThreadPoolExecutor、ReentrantLock 和 ReentrantReadWriteLock 内部都用到了它。…

理解位图算法:使用 C++ 实现高效数据查重

在处理海量数据时&#xff0c;我们常常需要检查某个元素是否已经存在于集合中。传统的方法如哈希表或集合容器虽然有效&#xff0c;但在数据量极大的情况下会占用大量内存。这时&#xff0c;位图算法 (Bitmap) 就成为了一种非常高效的解决方案。本文将通过分析一段使用位图算法…

数学复习笔记 12

前言 现在做一下例题和练习题。矩阵的秩和线性相关。另外还要复盘前面高数的部分的内容。奥&#xff0c;之前矩阵的例题和练习题&#xff0c;也没有做完&#xff0c;行列式的例题和练习题也没有做完。累加起来了。以后还是得学一个知识点就做一个部分的内容&#xff0c;日拱一…

1-10 目录树

在ZIP归档文件中&#xff0c;保留着所有压缩文件和目录的相对路径和名称。当使用WinZIP等GUI软件打开ZIP归档文件时&#xff0c;可以从这些信息中重建目录的树状结构。请编写程序实现目录的树状结构的重建工作。 输入格式: 输入首先给出正整数N&#xff08;≤104&#xff09;…