Bootstrap如何上传图片

发布时间:2021-06-24 13:37:19 作者:chen
来源:亿速云 阅读:392
# Bootstrap如何上传图片

## 前言

在现代Web开发中,图片上传是几乎所有内容管理系统(CMS)、社交平台和电子商务网站都需要的核心功能。Bootstrap作为最流行的前端框架之一,虽然不直接提供文件上传组件,但可以与其他技术完美结合实现这一功能。本文将全面介绍如何在Bootstrap项目中实现图片上传功能,涵盖从基础实现到高级优化的完整方案。

## 一、基础文件上传实现

### 1.1 HTML表单设置

Bootstrap提供了样式化的表单控件,我们可以利用`form-control`类来美化文件上传输入框:

```html
<div class="container mt-5">
  <form id="uploadForm" enctype="multipart/form-data">
    <div class="mb-3">
      <label for="imageUpload" class="form-label">选择图片文件</label>
      <input class="form-control" type="file" id="imageUpload" accept="image/*">
    </div>
    <button type="submit" class="btn btn-primary">上传图片</button>
  </form>
</div>

关键点说明: - enctype="multipart/form-data" 是文件上传必需的属性 - accept="image/*" 限制只能选择图片文件类型 - Bootstrap 5的表单控件类(form-control, form-label等)提供了现代化的样式

1.2 基本JavaScript处理

document.getElementById('uploadForm').addEventListener('submit', function(e) {
  e.preventDefault();
  
  const fileInput = document.getElementById('imageUpload');
  const file = fileInput.files[0];
  
  if (!file) {
    alert('请选择图片文件');
    return;
  }
  
  // 简单的客户端验证
  if (!file.type.match('image.*')) {
    alert('请选择有效的图片文件');
    return;
  }
  
  // 创建FormData对象
  const formData = new FormData();
  formData.append('image', file);
  
  // 发送到服务器
  fetch('/upload', {
    method: 'POST',
    body: formData
  })
  .then(response => response.json())
  .then(data => {
    console.log('上传成功:', data);
    // 显示上传成功的图片
    if(data.filePath) {
      const imgPreview = document.createElement('img');
      imgPreview.src = data.filePath;
      imgPreview.className = 'img-fluid mt-3';
      document.getElementById('uploadForm').appendChild(imgPreview);
    }
  })
  .catch(error => {
    console.error('上传失败:', error);
  });
});

1.3 服务器端处理示例(Node.js)

const express = require('express');
const multer = require('multer');
const path = require('path');

const app = express();
const upload = multer({ dest: 'uploads/' });

app.post('/upload', upload.single('image'), (req, res) => {
  if (!req.file) {
    return res.status(400).json({ error: '未上传文件' });
  }
  
  // 返回文件信息
  res.json({
    originalname: req.file.originalname,
    filename: req.file.filename,
    filePath: `/uploads/${req.file.filename}`
  });
});

// 提供静态文件访问
app.use('/uploads', express.static(path.join(__dirname, 'uploads')));

二、增强用户体验

2.1 拖放上传实现

使用Bootstrap结合HTML5拖放API可以创建更友好的上传体验:

<div class="container mt-5">
  <div id="dropArea" class="border rounded p-5 text-center">
    <i class="bi bi-cloud-arrow-up fs-1"></i>
    <h4>拖放图片到此处</h4>
    <p class="text-muted">或</p>
    <button class="btn btn-outline-primary">选择文件</button>
    <input type="file" id="dragDropUpload" accept="image/*" class="d-none">
  </div>
  <div id="previewContainer" class="mt-3 row"></div>
</div>

JavaScript处理代码:

const dropArea = document.getElementById('dropArea');
const fileInput = document.getElementById('dragDropUpload');

// 防止默认拖放行为
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
  dropArea.addEventListener(eventName, preventDefaults, false);
});

function preventDefaults(e) {
  e.preventDefault();
  e.stopPropagation();
}

// 高亮显示拖放区域
['dragenter', 'dragover'].forEach(eventName => {
  dropArea.addEventListener(eventName, highlight, false);
});

['dragleave', 'drop'].forEach(eventName => {
  dropArea.addEventListener(eventName, unhighlight, false);
});

function highlight() {
  dropArea.classList.add('border-primary');
  dropArea.style.backgroundColor = 'rgba(13, 110, 253, 0.1)';
}

function unhighlight() {
  dropArea.classList.remove('border-primary');
  dropArea.style.backgroundColor = '';
}

// 处理拖放文件
dropArea.addEventListener('drop', handleDrop, false);

function handleDrop(e) {
  const dt = e.dataTransfer;
  const files = dt.files;
  handleFiles(files);
}

// 点击按钮触发文件选择
dropArea.querySelector('button').addEventListener('click', () => {
  fileInput.click();
});

fileInput.addEventListener('change', function() {
  handleFiles(this.files);
});

function handleFiles(files) {
  [...files].forEach(uploadFile);
}

function uploadFile(file) {
  if (!file.type.match('image.*')) return;
  
  // 显示预览
  const reader = new FileReader();
  reader.onload = function(e) {
    const preview = document.createElement('div');
    preview.className = 'col-md-4 mb-3';
    preview.innerHTML = `
      <div class="card">
        <img src="${e.target.result}" class="card-img-top">
        <div class="card-body">
          <p class="card-text">${file.name}</p>
          <div class="progress">
            <div class="progress-bar" role="progressbar" style="width: 0%"></div>
          </div>
        </div>
      </div>
    `;
    document.getElementById('previewContainer').appendChild(preview);
    
    // 上传文件
    const formData = new FormData();
    formData.append('image', file);
    
    const xhr = new XMLHttpRequest();
    xhr.open('POST', '/upload', true);
    
    // 上传进度
    xhr.upload.onprogress = function(e) {
      if (e.lengthComputable) {
        const percentComplete = (e.loaded / e.total) * 100;
        preview.querySelector('.progress-bar').style.width = percentComplete + '%';
        preview.querySelector('.progress-bar').textContent = Math.round(percentComplete) + '%';
      }
    };
    
    xhr.onload = function() {
      if (xhr.status === 200) {
        const response = JSON.parse(xhr.responseText);
        preview.querySelector('.progress-bar').classList.add('bg-success');
        // 可以在这里添加更多上传成功后的处理
      } else {
        preview.querySelector('.progress-bar').classList.add('bg-danger');
        console.error('上传失败');
      }
    };
    
    xhr.send(formData);
  };
  reader.readAsDataURL(file);
}

2.2 图片预览与裁剪

使用Cropper.js库可以在上传前实现图片裁剪:

<div class="modal fade" id="cropModal" tabindex="-1">
  <div class="modal-dialog modal-lg">
    <div class="modal-content">
      <div class="modal-header">
        <h5 class="modal-title">裁剪图片</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
      </div>
      <div class="modal-body">
        <img id="cropImage" src="" class="img-fluid">
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
        <button type="button" class="btn btn-primary" id="cropButton">裁剪并上传</button>
      </div>
    </div>
  </div>
</div>

JavaScript代码:

let cropper;

document.getElementById('imageUpload').addEventListener('change', function(e) {
  const files = e.target.files;
  if (files && files.length > 0) {
    const file = files[0];
    const reader = new FileReader();
    
    reader.onload = function(e) {
      const image = document.getElementById('cropImage');
      image.src = e.target.result;
      
      // 初始化模态框
      const modal = new bootstrap.Modal(document.getElementById('cropModal'));
      modal.show();
      
      // 初始化Cropper.js
      if (cropper) {
        cropper.destroy();
      }
      cropper = new Cropper(image, {
        aspectRatio: 1, // 正方形
        viewMode: 1,
        autoCropArea: 0.8
      });
    };
    
    reader.readAsDataURL(file);
  }
});

document.getElementById('cropButton').addEventListener('click', function() {
  if (cropper) {
    // 获取裁剪后的图片
    cropper.getCroppedCanvas().toBlob(function(blob) {
      const formData = new FormData();
      formData.append('image', blob, 'cropped-image.jpg');
      
      // 上传裁剪后的图片
      fetch('/upload', {
        method: 'POST',
        body: formData
      })
      .then(response => response.json())
      .then(data => {
        console.log('上传成功:', data);
        // 关闭模态框
        const modal = bootstrap.Modal.getInstance(document.getElementById('cropModal'));
        modal.hide();
        // 显示上传结果
        showUploadResult(data.filePath);
      })
      .catch(error => {
        console.error('上传失败:', error);
      });
    });
  }
});

function showUploadResult(imageUrl) {
  const resultDiv = document.createElement('div');
  resultDiv.className = 'mt-3';
  resultDiv.innerHTML = `
    <div class="alert alert-success">上传成功!</div>
    <img src="${imageUrl}" class="img-thumbnail" style="max-height: 200px;">
  `;
  document.getElementById('uploadForm').appendChild(resultDiv);
}

三、高级功能实现

3.1 多文件上传与队列管理

<div class="container mt-5">
  <div class="card">
    <div class="card-header">
      <h5 class="mb-0">批量图片上传</h5>
    </div>
    <div class="card-body">
      <input type="file" id="multiUpload" accept="image/*" multiple class="d-none">
      <button id="selectFilesBtn" class="btn btn-primary mb-3">选择多个文件</button>
      
      <div id="uploadQueue" class="list-group"></div>
      
      <div class="d-flex justify-content-between mt-3">
        <button id="startUploadBtn" class="btn btn-success">开始上传</button>
        <button id="clearQueueBtn" class="btn btn-outline-danger">清空队列</button>
      </div>
    </div>
  </div>
</div>

JavaScript实现:

const fileQueue = [];
const MAX_PARALLEL_UPLOADS = 3; // 最大并行上传数
let currentUploads = 0;

document.getElementById('selectFilesBtn').addEventListener('click', function() {
  document.getElementById('multiUpload').click();
});

document.getElementById('multiUpload').addEventListener('change', function(e) {
  addFilesToQueue(e.target.files);
  e.target.value = ''; // 重置input,允许重复选择相同文件
});

document.getElementById('startUploadBtn').addEventListener('click', startUpload);
document.getElementById('clearQueueBtn').addEventListener('click', clearQueue);

function addFilesToQueue(files) {
  for (let i = 0; i < files.length; i++) {
    const file = files[i];
    if (!file.type.match('image.*')) continue;
    
    fileQueue.push({
      file: file,
      status: 'pending',
      progress: 0
    });
  }
  renderQueue();
}

function renderQueue() {
  const queueContainer = document.getElementById('uploadQueue');
  queueContainer.innerHTML = '';
  
  fileQueue.forEach((item, index) => {
    const itemElement = document.createElement('div');
    itemElement.className = `list-group-item list-group-item-action d-flex justify-content-between align-items-center`;
    
    let statusClass = '';
    if (item.status === 'uploading') statusClass = 'list-group-item-info';
    else if (item.status === 'success') statusClass = 'list-group-item-success';
    else if (item.status === 'error') statusClass = 'list-group-item-danger';
    
    itemElement.classList.add(statusClass);
    
    itemElement.innerHTML = `
      <div class="d-flex align-items-center">
        <span class="badge bg-secondary me-2">${index + 1}</span>
        <span>${item.file.name}</span>
      </div>
      <div>
        <span class="badge bg-${item.status === 'error' ? 'danger' : 'primary'} me-2">
          ${getStatusText(item.status)}
        </span>
        ${item.status === 'uploading' ? `
          <div class="progress" style="width: 100px; height: 20px;">
            <div class="progress-bar" role="progressbar" style="width: ${item.progress}%"></div>
          </div>
        ` : ''}
      </div>
    `;
    
    queueContainer.appendChild(itemElement);
  });
}

function getStatusText(status) {
  const statusMap = {
    'pending': '等待中',
    'uploading': '上传中',
    'success': '成功',
    'error': '失败'
  };
  return statusMap[status] || status;
}

function startUpload() {
  if (fileQueue.length === 0) {
    alert('队列中没有文件');
    return;
  }
  
  // 检查是否有待上传的文件
  const pendingFiles = fileQueue.filter(item => item.status === 'pending');
  if (pendingFiles.length === 0) {
    alert('没有待上传的文件');
    return;
  }
  
  // 开始上传
  processQueue();
}

function processQueue() {
  // 检查并行上传数
  while (currentUploads < MAX_PARALLEL_UPLOADS) {
    const nextItem = fileQueue.find(item => item.status === 'pending');
    if (!nextItem) break;
    
    currentUploads++;
    uploadFile(nextItem);
  }
}

function uploadFile(queueItem) {
  queueItem.status = 'uploading';
  renderQueue();
  
  const formData = new FormData();
  formData.append('image', queueItem.file);
  
  const xhr = new XMLHttpRequest();
  xhr.open('POST', '/upload', true);
  
  xhr.upload.onprogress = function(e) {
    if (e.lengthComputable) {
      queueItem.progress = (e.loaded / e.total) * 100;
      renderQueue();
    }
  };
  
  xhr.onload = function() {
    currentUploads--;
    
    if (xhr.status === 200) {
      queueItem.status = 'success';
      const response = JSON.parse(xhr.responseText);
      queueItem.response = response;
    } else {
      queueItem.status = 'error';
    }
    
    renderQueue();
    processQueue(); // 处理队列中的下一个文件
  };
  
  xhr.onerror = function() {
    currentUploads--;
    queueItem.status = 'error';
    renderQueue();
    processQueue();
  };
  
  xhr.send(formData);
}

function clearQueue() {
  // 只清除未开始上传的文件
  fileQueue.forEach(item => {
    if (item.status === 'pending') {
      item.status = 'cancelled';
    }
  });
  
  // 过滤掉已取消的项目
  for (let i = fileQueue.length - 1; i >= 0; i--) {
    if (fileQueue[i].status === 'cancelled') {
      fileQueue.splice(i, 1);
    }
  }
  
  renderQueue();
}

3.2 图片压缩与优化

在客户端进行图片压缩可以显著减少上传时间和服务器存储空间:

”`javascript function compressImage(file, quality = 0.8, maxWidth = 1024, callback) { const reader = new FileReader(); reader.onload = function(event) { const img = new Image(); img.onload = function() { // 计算新尺寸 let width = img.width; let height = img.height;

  if (width > maxWidth) {
    height = (maxWidth / width) * height;
    width = maxWidth;
  }

  // 创建canvas进行压缩
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  canvas.width = width;
  canvas.height = height;
  ctx.drawImage(img, 0, 0, width, height);

  // 转换为Blob
  canvas.toBlob(function(blob) {
    callback(blob);
  }, 'image/jpeg', quality);
};
img.src = event.target.result;

}; reader.readAsDataURL(file); }

// 使用示例 document.getElementById(‘uploadForm’).addEventListener(‘submit’, function(e) { e.preventDefault();

const fileInput = document.getElementById(‘imageUpload’); const file = fileInput.files[0];

if (!file) { alert(‘请选择图片文件’); return; }

// 显示加载状态 const submitBtn = e.target.querySelector(‘button[type=“submit”]’); const originalText = submitBtn.textContent; submitBtn.disabled = true; submitBtn.innerHTML = ‘ 处理中…’;

// 压缩图片 compressImage(file, 0.7, 1024, function(compressedBlob) { const formData = new FormData(); formData.append(‘image’, compressedBlob, file.name);

fetch('/upload', {
  method: 'POST',
  body: formData
推荐阅读:
  1. php实现上传图片
  2. django上传图片

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

bootstrap

上一篇:web信息传送使用什么协议

下一篇:Angular组件间进行交互的方法有哪些

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》