压缩包网页预览(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.mzph.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 名同学…