記錄一次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
繁體地址,請注明出處:http://hk.pswp.cn/web/80129.shtml
英文地址,請注明出處:http://en.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;…