壓縮包網頁預覽(zip-html-preview)

zip-html-preview

項目介紹

這是一個基于 Spring Boot 開發的在線 ZIP 文件預覽工具,主要用于預覽 ZIP 壓縮包中的 HTML 文件及其相關資源。

主要功能

  • 支持拖拽上傳或點擊選擇多個 ZIP 文件
  • 自動解壓并提取 ZIP 文件中的 HTML 文件
  • 在線預覽 HTML 文件及其相關的 CSS、JavaScript 和圖片等資源
  • 支持多文件批量處理

技術棧

  • 后端:Spring Boot 2.3.4
  • 前端:HTML5 + JavaScript
  • 構建工具:Maven

快速開始

環境要求

  • JDK 8+
  • Maven 3.x

配置說明

application.properties 中配置解壓文件存儲路徑:

zip.extract.path=D:/unzip

運行步驟

  1. 克隆項目
git clone https://gitee.com/anxwefndu/zip-html-preview.git
  1. 進入項目目錄
cd zip-html-preview/SpringBoot
  1. 編譯打包
mvn clean package
  1. 運行項目
java -jar target/SpringBoot-1.0-SNAPSHOT-execute.jar
  1. 訪問應用
    打開瀏覽器訪問: http://localhost:8080

使用說明

  1. 打開網頁后,可以通過拖拽或點擊選擇按鈕上傳 ZIP 文件
  2. 支持同時選擇多個 ZIP 文件
  3. 上傳完成后會自動顯示可預覽的 HTML 文件列表
  4. 點擊文件名即可在新標簽頁中預覽對應的 HTML 文件

源碼下載

zip-html-preview

演示截圖

1.系統首頁
在這里插入圖片描述

2.預覽壓縮包
在這里插入圖片描述

3.預覽頁面1
在這里插入圖片描述

4.預覽頁面2
在這里插入圖片描述

核心源碼

SpringBoot/src/main/resources/static/index.html

<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>zip-html-preview</title><link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin><link href="https://fonts.googleapis.com/css2?family=Pacifico&display=swap" rel="stylesheet"><link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet"><script src="https://cdn.tailwindcss.com"></script><script>tailwind.config = {theme: {extend: {colors: {primary: '#4F46E5',secondary: '#6366F1'},borderRadius: {'none': '0px','sm': '2px',DEFAULT: '4px','md': '8px','lg': '12px','xl': '16px','2xl': '20px','3xl': '24px','full': '9999px','button': '4px'}}}}</script><style>body::-webkit-scrollbar {width: 0.5rem;}body::-webkit-scrollbar-track {background: #f1f1f1;border-radius: 0.5rem;}body::-webkit-scrollbar-thumb {background: #3498db;border-radius: 0.5rem;}body::-webkit-scrollbar-thumb:hover {background: #2980b9;}.drag-area {border: 2px dashed #E5E7EB;transition: all 0.3s ease;}.drag-area:hover {border-color: #4F46E5;}.file-input {display: none;}.button-hover {transition: transform 0.2s ease;}.button-hover:active {transform: translateY(2px);}</style></head><body class="bg-gray-50"><div class="w-[1200px] mx-auto"><nav class="h-16 bg-white shadow-sm flex items-center justify-between px-8"><div class="flex items-center space-x-2"><span class="text-2xl font-['Pacifico'] text-primary">logo</span><span class="text-lg font-medium">zip-html-preview</span></div><button class="w-10 h-10 rounded-button flex items-center justify-center hover:bg-gray-100 transition-colors"><i class="fas fa-sun text-gray-600"></i></button></nav><main class="px-8 py-12"><div class="max-w-3xl mx-auto"><div class="bg-white rounded-lg shadow-sm p-8"><div id="dropArea" class="drag-area rounded-lg p-12 text-center"><input type="file" id="fileInput" class="file-input" multiple accept=".zip"><div class="space-y-4"><div class="w-20 h-20 mx-auto bg-gray-50 rounded-full flex items-center justify-center"><i class="fas fa-cloud-upload-alt text-4xl text-gray-400"></i></div><div><h3 class="text-lg font-medium">拖拽文件到這里,或</h3><button class="mt-2 px-6 py-2 bg-primary text-white rounded-button hover:bg-primary/90 button-hover whitespace-nowrap">點擊選擇文件</button></div><p class="text-sm text-gray-500">支持的文件格式:ZIP</p></div></div><div id="fileList" class="hidden mt-8 space-y-4"><div class="text-lg font-medium mb-4">已選擇的文件</div><div class="space-y-3"></div><div class="flex justify-end space-x-3 mt-6"><button id="clearButton" class="px-6 py-2 text-gray-600 bg-gray-100 rounded-button hover:bg-gray-200 button-hover whitespace-nowrap">清空列表</button><button id="previewButton" class="px-6 py-2 text-white bg-primary rounded-button hover:bg-primary/90 button-hover whitespace-nowrap">預覽</button></div></div><div id="previewLinks" class="hidden mt-8 space-y-4"><div class="text-lg font-medium mb-4">可預覽的文件</div><ul class="space-y-2" id="htmlFilesList"></ul></div></div><div class="mt-12 bg-white rounded-lg shadow-sm p-8"><h2 class="text-xl font-medium mb-6">使用說明</h2><div class="space-y-4 text-gray-600"><div class="flex items-start space-x-3"><div class="w-6 h-6 rounded-full bg-primary/10 flex items-center justify-center flex-shrink-0 mt-0.5"><i class="fas fa-check text-sm text-primary"></i></div><p>支持拖拽上傳或點擊選擇文件,可以一次選擇多個ZIP文件</p></div><div class="flex items-start space-x-3"><div class="w-6 h-6 rounded-full bg-primary/10 flex items-center justify-center flex-shrink-0 mt-0.5"><i class="fas fa-check text-sm text-primary"></i></div><p>系統會自動解析ZIP文件中的HTML文件,并提供在線預覽功能</p></div><div class="flex items-start space-x-3"><div class="w-6 h-6 rounded-full bg-primary/10 flex items-center justify-center flex-shrink-0 mt-0.5"><i class="fas fa-check text-sm text-primary"></i></div><p>支持預覽ZIP包中的HTML文件及其相關的CSS、JavaScript和圖片等資源</p></div><div class="flex items-start space-x-3"><div class="w-6 h-6 rounded-full bg-primary/10 flex items-center justify-center flex-shrink-0 mt-0.5"><i class="fas fa-check text-sm text-primary"></i></div><p>提供文件樹視圖,方便瀏覽ZIP包中的文件結構和快速切換不同的HTML文件</p></div></div></div></div></main></div><script>const dropArea = document.getElementById('dropArea');const fileInput = document.getElementById('fileInput');const fileList = document.getElementById('fileList');const previewButton = document.getElementById('previewButton');const clearButton = document.getElementById('clearButton');const previewLinks = document.getElementById('previewLinks');const htmlFilesList = document.getElementById('htmlFilesList');// 存儲所有文件的數組let selectedFiles = [];// 點擊拖拽區域觸發文件選擇dropArea.addEventListener('click', () => {fileInput.click();});// 拖拽進入時改變樣式dropArea.addEventListener('dragover', (e) => {e.preventDefault();dropArea.classList.add('border-primary');});// 拖拽離開時恢復樣式dropArea.addEventListener('dragleave', () => {dropArea.classList.remove('border-primary');});// 拖拽放下時處理文件dropArea.addEventListener('drop', (e) => {e.preventDefault();dropArea.classList.remove('border-primary');const files = e.dataTransfer.files;handleFiles(files);});// 文件輸入框變化時處理文件fileInput.addEventListener('change', () => {const files = fileInput.files;handleFiles(files);fileInput.value = null;});// 處理文件并更新文件列表function handleFiles(files) {for (let i = 0; i < files.length; i++) {const file = files[i];if (!selectedFiles.some(f => f.name === file.name)) {selectedFiles.push(file);}}updateFileList();}// 更新文件列表 DOMfunction updateFileList() {if (selectedFiles.length > 0) {fileList.classList.remove('hidden');} else {fileList.classList.add('hidden');}const listContainer = fileList.querySelector('.space-y-3');listContainer.innerHTML = ''; // 清空舊的文件列表selectedFiles.forEach((file, index) => {const fileType = file.type || 'text/plain';const fileSize = formatFileSize(file.size);const listItem = document.createElement('div');listItem.className = 'flex items-center justify-between p-4 bg-gray-50 rounded-lg';// 左側:文件圖標和信息const leftSide = document.createElement('div');leftSide.className = 'flex items-center space-x-3';const icon = document.createElement('i');icon.className = getIconClass(fileType);icon.style.color = '#4F46E5'; // 設置顏色const fileInfo = document.createElement('div');fileInfo.innerHTML = `<div class="font-medium">${escapeHTML(file.name)}</div><div class="text-sm text-gray-500">${fileSize}</div>`;leftSide.appendChild(icon);leftSide.appendChild(fileInfo);// 右側:操作按鈕const rightSide = document.createElement('div');rightSide.className = 'flex items-center space-x-2';// 刪除按鈕const deleteButton = document.createElement('button');deleteButton.className = 'p-2 text-gray-400 hover:text-gray-600 rounded-button button-hover';deleteButton.innerHTML = '<i class="fas fa-times"></i>';deleteButton.addEventListener('click', () => removeFile(index));rightSide.appendChild(deleteButton);listItem.appendChild(leftSide);listItem.appendChild(rightSide);listContainer.appendChild(listItem);});}// 格式化文件大小function formatFileSize(size) {const units = ['B', 'KB', 'MB', 'GB'];let unitIndex = 0;while (size >= 1024 && unitIndex < units.length - 1) {size /= 1024;unitIndex++;}return `${size.toFixed(2)} ${units[unitIndex]}`;}// 獲取文件類型的圖標類名function getIconClass(type) {if (type.startsWith('image/')) {return 'fas fa-file-image text-primary w-8 h-8 flex items-center justify-center';} else if (type === 'text/plain' || type.endsWith('.md')) {return 'fas fa-file-alt text-primary w-8 h-8 flex items-center justify-center';} else {return 'fas fa-file text-primary w-8 h-8 flex items-center justify-center';}}// 移除指定索引的文件function removeFile(index) {selectedFiles.splice(index, 1);updateFileList();}// 清空所有文件document.querySelector('#fileList .px-6.py-2.text-gray-600').addEventListener('click', () => {selectedFiles = [];updateFileList();});// 轉義 HTML 特殊字符function escapeHTML(str) {return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');}// 清空所有文件clearButton.addEventListener('click', () => {selectedFiles = [];updateFileList();previewLinks.classList.add('hidden');htmlFilesList.innerHTML = '';});// 預覽按鈕點擊事件previewButton.addEventListener('click', () => {if (selectedFiles.length === 0) return;const formData = new FormData();selectedFiles.forEach(file => formData.append('file', file));fetch('/api/upload', {method: 'POST',body: formData}).then(response => response.json()).then(data => {previewLinks.classList.remove('hidden');htmlFilesList.innerHTML = '';data.forEach(htmlFile => {const listItem = document.createElement('li');listItem.className = 'flex items-center justify-between p-4 bg-gray-50 rounded-lg';const fileName = document.createElement('a');fileName.href = htmlFile.filePath;fileName.target = '_blank';fileName.className = 'text-blue-500 hover:underline';fileName.textContent = htmlFile.fileName;listItem.appendChild(fileName);htmlFilesList.appendChild(listItem);});}).catch(error => {alert('上傳失敗,請稍后再試。');console.error('Error:', error);});});</script></body></html>

SpringBoot/src/main/java/com/boot/service/ZipService.java

package com.boot.service;import com.boot.config.ZipConfig;
import com.boot.model.HtmlFile;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.*;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;@Service
public class ZipService {@Autowiredprivate ZipConfig zipConfig;public List<HtmlFile> processZipFile(MultipartFile file) throws IOException {String extractDir = zipConfig.getExtract().getPath();File dir = new File(extractDir);if (!dir.exists()) {dir.mkdirs();}// 解壓文件try (ZipInputStream zis = new ZipInputStream(file.getInputStream(), Charset.forName("GBK"))) {ZipEntry zipEntry;byte[] buffer = new byte[1024];while ((zipEntry = zis.getNextEntry()) != null) {File newFile = new File(extractDir + File.separator + zipEntry.getName());if (zipEntry.isDirectory()) {newFile.mkdirs();continue;}// 創建父目錄new File(newFile.getParent()).mkdirs();// 寫入文件try (FileOutputStream fos = new FileOutputStream(newFile)) {int len;while ((len = zis.read(buffer)) > 0) {fos.write(buffer, 0, len);}}}}// 查找第一層的HTML文件List<HtmlFile> htmlFiles = new ArrayList<>();// 提取文件夾名稱(去掉擴展名)String folderName = file.getOriginalFilename().substring(0, file.getOriginalFilename().lastIndexOf('.'));extractDir = extractDir + File.separator + folderName;try (DirectoryStream<Path> stream = Files.newDirectoryStream(Paths.get(extractDir))) {for (Path path : stream) {if (Files.isRegularFile(path) && path.toString().toLowerCase().endsWith(".html")) {htmlFiles.add(new HtmlFile(path.getFileName().toString(), "/preview" + path.toString().substring(zipConfig.getExtract().getPath().length())));}}}return htmlFiles;}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/77167.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/77167.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/77167.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

QML之Overlay

Overlay&#xff08;覆蓋層&#xff09;是QML中用于在當前界面之上顯示臨時內容的重要組件。 一、Overlay基礎概念 1.1 什么是Overlay&#xff1f; Overlay是一種浮動在現有界面之上的視覺元素&#xff0c;具有以下特點&#xff1a; 臨時顯示&#xff0c;不影響底層布局 通…

iso17025證書申請方法?iso17025認證意義

ISO/IEC 17025證書申請方法 ISO/IEC 17025是檢測和校準實驗室能力的國際標準&#xff0c;申請CNAS認可的流程如下&#xff1a; 1. 前期準備 標準學習&#xff1a;深入理解ISO/IEC 17025:2017標準要求。 差距分析&#xff1a;評估現有實驗室管理與技術能力與標準的差距。 制…

reverse3 1(Base加密)

題目 做法 下載安裝包&#xff0c;解壓&#xff0c;把解壓后的文件拖進Exeinfo PE進行分析 32位&#xff0c;無殼 扔進IDA&#xff08;32位&#xff09;&#xff0c;找到main&#xff0c;F5反編譯 只是因為在人群中多看了你一眼——第31行的right flag&#xff0c;關鍵詞找到…

電控---CMSIS概覽

1. CMSIS庫簡介 CMSIS&#xff08;Cortex Microcontroller Software Interface Standard&#xff0c;Cortex微控制器軟件接口標準&#xff09;是由ARM公司開發的一套標準化軟件接口&#xff0c;旨在為基于ARM Cortex-M系列處理器&#xff08;如Cortex-M0/M0/M3/M4/M7/M33等&am…

list.

列表類型是用來存儲多個有序的字符串&#xff0c;列表中的每個字符串稱為元素&#xff08;element&#xff09;&#xff0c;?個列表最多可以存儲個元素 在 Redis 中&#xff0c;可以對列表兩端插入&#xff08;push&#xff09;和彈出&#xff08;pop&#xff09;&#xff0c;…

關于Diamond機械手的運動學與動力學的推導

1.關于Diamond機械手 &#xff08;1&#xff09;位置模型推導 逆解&#xff1a;機械末端平臺的位置與驅動關節之間的關系。 設p點在xy平面的坐標是&#xff08;x&#xff0c;y&#xff09;T&#xff0c;此時根據向量求解 OP等于向量r等于e向xy軸的向量主動臂長度向xy軸的向量…

如何新建一個空分支(不繼承 master 或任何提交)

一、需求分析&#xff1a; 在 Git 中&#xff0c;我們通常通過 git branch 來新建分支&#xff0c;這些分支默認都會繼承當前所在分支的提交記錄。但有時候我們希望新建一個“完全干凈”的分支 —— 沒有任何提交&#xff0c;不繼承 master 或任何已有內容&#xff0c;這該怎么…

Flask(補充內容)配置SSL 證書 實現 HTTPS 服務

沒有加密的http服務&#xff0c;就像在裸泳&#xff0c;鉆到水里便將你看個精光。數據在互聯網上傳輸時&#xff0c;如果未經加密&#xff0c;隨時可能被抓包軟件抓住&#xff0c;里面的cookie、用戶名、密碼什么的&#xff0c;它會看得一清二楚&#xff0c;所以&#xff0c;只…

云服務器CVM標準型S5實例性能測評——2025騰訊云

騰訊云服務器CVM標準型S5實例具有穩定的計算性能&#xff0c;CPU采用采用 Intel Xeon Cascade Lake 或者 Intel Xeon Cooper Lake 處理器&#xff0c;主頻2.5GHz&#xff0c;睿頻3.1GHz&#xff0c;CPU內存配置2核2G、2核4G、4核8G、8核16G等配置&#xff0c;公網帶寬可選1M、3…

什么是智算中心

智算中心是一種專門為智能計算提供強大算力支持的基礎設施&#xff0c;以下是關于它的詳細介紹&#xff1a; 定義與功能 智算中心是基于強大的計算能力&#xff0c;特別是針對人工智能算法進行優化的計算中心。它集成了大量的高性能計算設備&#xff0c;如 GPU 集群、FPGA 陣…

注意力機制是如何實現的

注意力機制的實現可以分解為幾個核心步驟&#xff0c;其本質是通過動態計算權重&#xff0c;決定不同位置信息的重要性&#xff0c;再對信息進行加權融合。以下從數學原理、代碼實現到直觀解釋逐步展開&#xff1a; 一、核心實現步驟 以最常見的**點積注意力&#xff08;Dot-P…

【裁員感想】

裁員感想 今天忽然感覺很emo 因為知道公司要裁員 年中百分之10 年末百分十10 我知道這個百分20會打到自己 所以還挺不開心的 我就想起 我的一個親戚當了大學老師 我覺得真的挺好的 又有寒暑假 又不是很累 薪資也不低 又是編制 同時也覺得自己很失敗 因為對自己互聯網的工作又…

從信號處理角度理解圖像處理的濾波函數

目錄 1、預備知識 1.1 什么是LTI系統? 1.1.1 首先來看什么是線性系統,前提我們要了解什么是齊次性和疊加性。

目標檢測概述

為什么基于卷積網絡的目標檢測模型在預測后要使用非極大值抑制 基于卷積網絡的目標檢測模型可能會在目標的相鄰區域生成多個相互重疊框&#xff0c;每個框的預測結果都是同一個目標&#xff0c;引起同一目標的重復檢測。造成這一現象的原因主要有兩個&#xff0c; 基于卷積網絡…

【JAVA】在idea新加artifact時,點擊Build-Build Artifacts時,新加的artifact不能選中

首先保證添加artifact無問題&#xff0c;比如依賴都正確、無重復命令的情況等 辦法 一 File > Invalidate Caches / Restart。 重啟IDEA后&#xff0c;重新檢查Artifact是否可選 辦法 二 打開 Project Structure&#xff08;CtrlShiftAltS&#xff09;。 進入 Artifacts 選…

Paramiko 使用教程

目錄 簡介安裝 Paramiko連接到遠程服務器執行遠程命令文件傳輸示例 簡介 Paramiko 是一個基于 Python 的 SSH 客戶端庫&#xff0c;它提供了在網絡上安全傳輸文件和執行遠程命令的功能。本教程將介紹 Paramiko 的基本用法&#xff0c;包括連接到遠程服務器、執行命令、文件傳輸…

《TCP/IP網絡編程》學習筆記 | Chapter 24:制作 HTTP 服務器端

《TCP/IP網絡編程》學習筆記 | Chapter 24&#xff1a;制作 HTTP 服務器端 《TCP/IP網絡編程》學習筆記 | Chapter 24&#xff1a;制作 HTTP 服務器端HTTP 概要理解 Web 服務器端無狀態的 Stateless 協議請求消息&#xff08;Request Message&#xff09;的結構響應消息&#x…

【Quest開發】在虛擬世界設置具有遮擋關系的透視窗口

軟件&#xff1a;Unity 2022.3.51f1c1、vscode、Meta XR All in One SDK V72 硬件&#xff1a;Meta Quest3 僅針對urp管線 參考了YY老師這篇&#xff0c;可以先看他的再看這個可能更好理解一些&#xff1a;Unity Meta Quest MR 開發&#xff08;七&#xff09;&#xff1a;使…

GPU 招投標全流程分析與總結

GPU 招投標全流程分析與總結 招投標流程概述 以下是通過代理商采購Nvidia H20-GPU 141G的招投標全流程分析: #mermaid-svg-hMPPfkCpGj8GKXfV {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-hMPPfkCpGj8GKXfV .er…

[C++] STL中的向量容器<vector>附加練習

目錄 講在前面(必看)八卦陣題目描述輸入格式輸出格式輸入輸出樣例數據范圍AC代碼及要點 決賽應援題目描述輸入格式輸出格式輸入輸出樣例數據范圍AC代碼及要點 講在前面(必看) 本篇為練習篇, vector講解篇在這里. 菜鳥食用前請做好心理準備(你懂的) 八卦陣 題目描述 n 名同學…