You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

本地HTML/JS实时图片画廊:监控文件夹实现增删改及点击放大

本地实时监控文件夹的图片画廊解决方案

你的需求

  • 新增图片自动加入画廊
  • 删除图片自动从画廊移除
  • 修改图片自动刷新对应内容
  • 点击图片从原位置放大至全屏(不用整页刷新)

现有代码的问题

  1. 图片刷新逻辑错误:refresh函数接收的是getElementsByClassName返回的元素集合,但直接操作node.src——集合没有src属性,导致刷新完全失效。
  2. 无法感知文件变化:靠递增数字加载test-n.jpg的方式,只能加载连续命名的图片,文件夹里的新增/删除操作根本检测不到。
  3. 无图片状态维护:没记录当前画廊的图片列表,没法对比文件变化来新增或移除元素。

完整解决方案(前端+轻量后端)

因为浏览器出于安全限制,不让前端直接读取本地文件夹的变化,所以需要搭个轻量后端配合,用Node.js实现就行,步骤简单不用复杂配置。

步骤1:准备环境

  1. 先装Node.js,官网下载后一路下一步安装即可
  2. 在你的画廊文件夹下,新建server.js和package.json两个文件

步骤2:后端代码(server.js)

const http = require('http');
const fs = require('fs');
const path = require('path');
const chokidar = require('chokidar');
const WebSocket = require('ws');

// 图片文件夹路径,对应你原来的imgportfolio
const imgDir = path.join(__dirname, 'imgportfolio');
// 允许的图片格式,可按需添加
const imgExts = ['.jpg', '.jpeg', '.png', '.gif'];

// 创建静态文件服务器,托管HTML/CSS/图片等
const server = http.createServer((req, res) => {
  let filePath = req.url === '/' ? '/index.html' : req.url;
  filePath = path.join(__dirname, filePath);
  
  const ext = path.extname(filePath);
  let contentType = 'text/html';
  if (ext === '.css') contentType = 'text/css';
  if (ext === '.js') contentType = 'application/javascript';
  if (imgExts.includes(ext)) contentType = `image/${ext.slice(1)}`;

  fs.readFile(filePath, (err, data) => {
    if (err) {
      res.writeHead(404);
      res.end('文件不存在');
      return;
    }
    res.writeHead(200, { 'Content-Type': contentType });
    res.end(data);
  });
});

// 创建WebSocket服务,用来给前端推送文件变化
const wss = new WebSocket.Server({ server });

// 获取当前文件夹里的所有图片
function getImgList() {
  return fs.readdirSync(imgDir)
    .filter(file => imgExts.includes(path.extname(file).toLowerCase()))
    .map(file => `/imgportfolio/${file}`);
}

// 监听图片文件夹的变化
const watcher = chokidar.watch(imgDir, { ignoreInitial: false });
let currentImgList = getImgList();

watcher.on('all', (event, path) => {
  const newImgList = getImgList();
  // 把最新图片列表推送给所有打开页面的客户端
  wss.clients.forEach(client => {
    if (client.readyState === WebSocket.OPEN) {
      client.send(JSON.stringify({ imgList: newImgList }));
    }
  });
  currentImgList = newImgList;
});

// 启动服务器,端口设为3000
const PORT = 3000;
server.listen(PORT, () => {
  console.log(`服务器跑起来了,访问 http://localhost:${PORT}`);
});

步骤3:package.json(用来安装依赖)

{
  "name": "local-gallery",
  "version": "1.0.0",
  "dependencies": {
    "chokidar": "^3.5.3",
    "ws": "^8.14.2"
  }
}

步骤4:前端代码(index.html)

<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8">
  <title>实时图片画廊</title>
  <style>
    /* 基础画廊样式,可按你的需求修改 */
    .row {
      display: flex;
      flex-wrap: wrap;
      gap: 10px;
      padding: 20px;
    }
    .gallery-item {
      flex: 1 1 calc(25% - 10px);
      min-width: 200px;
      cursor: zoom-in;
    }
    .gallery-img img {
      width: 100%;
      height: auto;
      display: block;
      transition: transform 0.2s;
    }
    .gallery-img img:hover {
      transform: scale(1.02);
    }

    /* 全屏放大的弹窗样式 */
    .lightbox {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background: rgba(0,0,0,0.9);
      display: none;
      align-items: center;
      justify-content: center;
      z-index: 9999;
      cursor: zoom-out;
    }
    .lightbox.active {
      display: flex;
    }
    .lightbox img {
      max-width: 90%;
      max-height: 90%;
      transition: transform 0.3s ease;
    }
  </style>
</head>
<body>
  <div class="ajax-inserted-content">
    <section class="section gallery grid layout-4-col">
      <div class="container">
        <div class="row" id='gallery-items'></div>
      </div>
    </section>
  </div>

  <!-- 全屏放大的弹窗容器 -->
  <div class="lightbox" id="lightbox">
    <img id="lightbox-img" src="" alt="">
  </div>

  <script>
    const galleryContainer = document.getElementById('gallery-items');
    const lightbox = document.getElementById('lightbox');
    const lightboxImg = document.getElementById('lightbox-img');
    let currentImgList = [];

    // 渲染画廊:对比新旧列表,新增/移除图片元素
    function renderGallery(imgList) {
      // 先删掉画廊里已经不存在的图片
      const existingImgs = Array.from(galleryContainer.querySelectorAll('.gallery-item img'));
      existingImgs.forEach(img => {
        const imgPath = img.src.split(window.location.origin)[1];
        if (!imgList.includes(imgPath)) {
          img.closest('.gallery-item').parentElement.remove();
        }
      });

      // 添加新出现的图片
      imgList.forEach(imgUrl => {
        if (!currentImgList.includes(imgUrl)) {
          const colDiv = document.createElement('div');
          colDiv.className = 'col-3 col-lg-4 col-md-6 col-sm-12';
          colDiv.innerHTML = `
            <div class="gallery-item">
              <div class="gallery-img">
                <img class="img" src="${imgUrl}?t=${Date.now()}" alt="画廊图片">
              </div>
            </div>
          `;
          galleryContainer.appendChild(colDiv);

          // 绑定点击放大事件,实现从原位置放大的效果
          const img = colDiv.querySelector('img');
          img.addEventListener('click', () => {
            lightboxImg.src = img.src.split('?')[0];
            // 获取原图位置,设置初始缩放和位移
            const rect = img.getBoundingClientRect();
            const scale = rect.width / lightboxImg.offsetWidth;
            lightboxImg.style.transform = `translate(${rect.left}px, ${rect.top}px) scale(${scale})`;
            lightbox.classList.add('active');
            // 延迟触发过渡动画
            setTimeout(() => {
              lightboxImg.style.transform = 'translate(0,0) scale(1)';
            }, 10);
          });
        }
      });

      // 更新当前图片列表
      currentImgList = [...imgList];
    }

    // 关闭全屏弹窗时的动画
    lightbox.addEventListener('click', () => {
      const activeImg = galleryContainer.querySelector(`img[src^="${lightboxImg.src}"]`);
      if (activeImg) {
        const rect = activeImg.getBoundingClientRect();
        const scale = rect.width / lightboxImg.offsetWidth;
        lightboxImg.style.transform = `translate(${rect.left}px, ${rect.top}px) scale(${scale})`;
        setTimeout(() => {
          lightbox.classList.remove('active');
          lightboxImg.style.transform = '';
        }, 300);
      } else {
        lightbox.classList.remove('active');
      }
    });

    // 连接WebSocket,接收后端推送的图片变化
    const ws = new WebSocket(`ws://${window.location.host}`);
    ws.onmessage = (event) => {
      const data = JSON.parse(event.data);
      renderGallery(data.imgList);
    };

    // 定时刷新图片,解决修改后浏览器缓存的问题
    setInterval(() => {
      const imgs = galleryContainer.querySelectorAll('.img');
      imgs.forEach(img => {
        const srcWithoutTimestamp = img.src.split('?')[0];
        img.src = `${srcWithoutTimestamp}?t=${Date.now()}`;
      });
    }, 2000); // 每2秒检查一次图片是否更新
  </script>
</body>
</html>

使用方法

  1. 打开命令行,进入你的画廊文件夹,运行npm install安装依赖
  2. 运行node server.js启动服务器
  3. 在浏览器里打开http://localhost:3000,就能看到实时更新的画廊了

代码说明

  1. 后端:用Node.js搭了个静态文件服务器,同时用chokidar监听图片文件夹的变化,一旦有新增/删除/修改,就通过WebSocket把最新的图片列表推给前端。
  2. 前端:
    • 接收后端推送的图片列表,对比当前列表自动新增或移除画廊元素
    • 用CSS过渡动画实现了点击图片从原位置放大的全屏效果
    • 定时给图片URL加时间戳,解决浏览器缓存导致修改后图片不刷新的问题

内容的提问来源于stack exchange,提问作者Vincent Zorzi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 14:14:53