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

如何使用HTML/CSS/JavaScript实现本地文件选择并在网页列表展示?

实现方案全指南

前端和本地设备交互读取文件、展示列表完全基于浏览器原生 File API 实现,不需要额外引入第三方依赖,所有操作默认在浏览器本地完成,不会主动上传文件到服务器。

功能拆分实现

1. 标签切换模块

用两个按钮对应两个标签页,点击按钮切换对应内容区的显示状态即可。

2. 文件上传标签功能

支持点击选择和拖拽选择两种方式,核心逻辑是获取用户选中的File对象列表:

  • 点击选择:使用原生 <input type="file" multiple> 标签,将其设为透明隐藏,点击自定义按钮时触发该input的点击事件,监听input的change事件即可拿到选中的文件列表 e.target.files
  • 拖拽选择:给拖拽区域绑定三个事件即可实现:
    const dropArea = document.querySelector('.upload-area')
    // 阻止浏览器默认拖拽行为(否则会直接打开文件)
    ['dragover', 'dragleave', 'drop'].forEach(evt => {
      dropArea.addEventListener(evt, e => e.preventDefault())
    })
    // 拖拽悬浮样式切换
    dropArea.addEventListener('dragover', () => dropArea.classList.add('hover'))
    dropArea.addEventListener('dragleave', () => dropArea.classList.remove('hover'))
    // 接收拖拽的文件
    dropArea.addEventListener('drop', e => {
      const files = e.dataTransfer.files
      processFiles(files)
      dropArea.classList.remove('hover')
    })
    

3. 可用文件列表展示功能

拿到File对象后,可以直接读取内置属性拼接列表:

  • 存储文件:定义全局数组 const fileList = [] 存储所有选中的文件,新增文件前可根据文件名+文件大小+最后修改时间判断去重,避免重复添加
  • 格式化展示字段:
    • 文件名:file.name
    • 文件大小:将file.size(单位字节)转成KB/MB格式,示例转换函数:
      function formatFileSize(bytes) {
        if (bytes < 1024) return `${bytes} B`
        if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KB`
        return `${(bytes / (1024 * 1024)).toFixed(2)} MB`
      }
      
    • 修改时间:new Date(file.lastModified).toLocaleString() 转成本地可读时间格式
    • 文件类型:file.type 或者截取文件名后缀判断
  • 渲染列表:遍历fileList数组,把每个文件的属性拼接成DOM元素插入到列表容器即可。

最小可运行示例

直接复制下面代码保存为html文件打开即可看到完整效果:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>文件管理</title>
  <style>
    * {margin: 0; padding: 0; box-sizing: border-box; font-family: sans-serif;}
    .container {max-width: 800px; margin: 30px auto; padding: 0 20px;}
    /* 标签样式 */
    .tabs {display: flex; border-bottom: 1px solid #eee; margin-bottom: 20px;}
    .tab-btn {padding: 12px 24px; border: none; background: none; font-size: 16px; cursor: pointer; border-bottom: 2px solid transparent;}
    .tab-btn.active {border-bottom-color: #2385bb; color: #2385bb; font-weight: 500;}
    .tab-content {display: none;}
    .tab-content.active {display: block;}
    /* 上传区域样式 */
    .upload-area {border: 2px dashed #ccc; border-radius: 8px; padding: 60px 20px; text-align: center; cursor: pointer;}
    .upload-area.hover {border-color: #2385bb; background: #f0f8ff;}
    .upload-area input {display: none;}
    /* 文件列表样式 */
    .file-item {display: flex; justify-content: space-between; align-items: center; padding: 12px; border-bottom: 1px solid #eee;}
    .file-info {display: flex; flex-direction: column; gap: 4px;}
    .file-name {font-weight: 500;}
    .file-meta {font-size: 14px; color: #666; display: flex; gap: 12px;}
  </style>
</head>
<body>
  <div class="container">
    <div class="tabs">
      <button class="tab-btn active" data-tab="upload">文件上传</button>
      <button class="tab-btn" data-tab="files">可用文件</button>
    </div>
    <!-- 上传标签内容 -->
    <div class="tab-content active" id="upload">
      <div class="upload-area">
        <input type="file" id="fileInput" multiple>
        <p>拖拽文件到此处,或点击选择本地文件</p>
      </div>
    </div>
    <!-- 文件列表标签内容 -->
    <div class="tab-content" id="files">
      <div id="fileList"></div>
    </div>
  </div>

  <script>
    const fileList = []
    const tabBtns = document.querySelectorAll('.tab-btn')
    const tabContents = document.querySelectorAll('.tab-content')
    const uploadArea = document.querySelector('.upload-area')
    const fileInput = document.getElementById('fileInput')
    const fileListDom = document.getElementById('fileList')

    // 标签切换逻辑
    tabBtns.forEach(btn => {
      btn.addEventListener('click', () => {
        const targetTab = btn.dataset.tab
        tabBtns.forEach(b => b.classList.remove('active'))
        tabContents.forEach(c => c.classList.remove('active'))
        btn.classList.add('active')
        document.getElementById(targetTab).classList.add('active')
      })
    })

    // 点击上传区域触发文件选择
    uploadArea.addEventListener('click', () => fileInput.click())
    fileInput.addEventListener('change', e => processFiles(e.target.files))

    // 拖拽逻辑
    ['dragover', 'dragleave', 'drop'].forEach(evt => {
      uploadArea.addEventListener(evt, e => e.preventDefault())
    })
    uploadArea.addEventListener('dragover', () => uploadArea.classList.add('hover'))
    uploadArea.addEventListener('dragleave', () => uploadArea.classList.remove('hover'))
    uploadArea.addEventListener('drop', e => {
      processFiles(e.dataTransfer.files)
      uploadArea.classList.remove('hover')
    })

    // 处理选中的文件
    function processFiles(files) {
      Array.from(files).forEach(file => {
        // 简单去重:判断文件名+大小+修改时间一致则不添加
        const isExist = fileList.some(item => 
          item.name === file.name && item.size === file.size && item.lastModified === file.lastModified
        )
        if (!isExist) fileList.push(file)
      })
      renderFileList()
    }

    // 格式化文件大小
    function formatFileSize(bytes) {
      if (bytes < 1024) return `${bytes} B`
      if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(2)} KB`
      return `${(bytes / (1024 * 1024)).toFixed(2)} MB`
    }

    // 渲染文件列表
    function renderFileList() {
      if (fileList.length === 0) {
        fileListDom.innerHTML = '<p style="text-align: center; padding: 40px 0; color: #999;">暂无文件</p>'
        return
      }
      fileListDom.innerHTML = fileList.map(file => `
        <div class="file-item">
          <div class="file-info">
            <div class="file-name">${file.name}</div>
            <div class="file-meta">
              <span>${formatFileSize(file.size)}</span>
              <span>${new Date(file.lastModified).toLocaleString()}</span>
              <span>${file.type || '未知类型'}</span>
            </div>
          </div>
        </div>
      `).join('')
    }

    // 初始化渲染空列表
    renderFileList()
  </script>
</body>
</html>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 14:36:04