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

如何使用PHP将多组文本与图片上传至MySQL数据库?

实现动态文本+图片交替的选择题内容方案(PHP+MySQL)

一、数据库设计

采用双表关联存储,适配任意数量的文本/图片组合:

  1. 问题主表 questions

    字段名类型说明
    idINT(11)主键,自增
    titleVARCHAR(255)题目标题
    created_atDATETIME创建时间(可选)
  2. 问题内容项表 question_content

    字段名类型说明
    idINT(11)主键,自增
    question_idINT(11)外键,关联questions.id
    typeENUM('text','image')内容类型:文本/图片
    contentTEXT文本内容或图片路径
    sort_orderINT(11)排序值,保证内容顺序

二、前端实现:动态内容块+图片上传

核心是做可动态增删的内容块,支持文本输入、图片拖放/粘贴上传。

1. HTML结构

<form id="question-form" method="POST" action="save-question.php">
  <div>
    <label>题目标题:</label>
    <input type="text" name="title" required>
  </div>
  
  <div id="content-container">
    <!-- 默认初始内容块 -->
    <div class="content-block" data-order="0">
      <select class="content-type" name="content_type[]" required>
        <option value="text">文本</option>
        <option value="image">图片</option>
      </select>
      <textarea class="text-content" name="text_content[]" placeholder="输入文本内容"></textarea>
      <div class="image-upload-area" style="display: none; border: 1px dashed #ccc; padding: 20px; margin: 10px 0;">
        <p>拖放图片到这里,或直接粘贴图片</p>
        <img class="preview-image" src="" style="max-width: 300px; display: none;" />
        <input type="hidden" class="image-path" name="image_path[]" />
      </div>
      <button type="button" class="remove-block">删除</button>
    </div>
  </div>
  
  <button type="button" id="add-block">添加内容块</button>
  <button type="submit">保存题目</button>
</form>

2. JavaScript逻辑

处理动态块增删、类型切换、图片上传:

// 内容类型切换
document.addEventListener('change', function(e) {
  if (e.target.classList.contains('content-type')) {
    const block = e.target.closest('.content-block');
    const textArea = block.querySelector('.text-content');
    const imageArea = block.querySelector('.image-upload-area');
    
    if (e.target.value === 'text') {
      textArea.style.display = 'block';
      imageArea.style.display = 'none';
      textArea.setAttribute('required', true);
      block.querySelector('.image-path').removeAttribute('required');
    } else {
      textArea.style.display = 'none';
      imageArea.style.display = 'block';
      textArea.removeAttribute('required');
      block.querySelector('.image-path').setAttribute('required', true);
    }
  }
});

// 添加新内容块
document.getElementById('add-block').addEventListener('click', function() {
  const container = document.getElementById('content-container');
  const lastBlock = container.querySelector('.content-block:last-child');
  const newOrder = parseInt(lastBlock.dataset.order) + 1;
  
  const newBlock = lastBlock.cloneNode(true);
  newBlock.dataset.order = newOrder;
  
  // 清空内容
  newBlock.querySelector('.text-content').value = '';
  newBlock.querySelector('.image-path').value = '';
  newBlock.querySelector('.preview-image').src = '';
  newBlock.querySelector('.preview-image').style.display = 'none';
  
  container.appendChild(newBlock);
});

// 删除内容块
document.addEventListener('click', function(e) {
  if (e.target.classList.contains('remove-block')) {
    const blocks = document.querySelectorAll('.content-block');
    if (blocks.length > 1) {
      e.target.closest('.content-block').remove();
      // 更新剩余块排序值
      blocks.forEach((block, index) => block.dataset.order = index);
    }
  }
});

// 拖放图片上传
document.addEventListener('dragover', function(e) {
  const uploadArea = e.target.closest('.image-upload-area');
  if (uploadArea) {
    e.preventDefault();
    uploadArea.style.backgroundColor = '#f0f0f0';
  }
});

document.addEventListener('dragleave', function(e) {
  const uploadArea = e.target.closest('.image-upload-area');
  if (uploadArea) {
    uploadArea.style.backgroundColor = 'transparent';
  }
});

document.addEventListener('drop', function(e) {
  const uploadArea = e.target.closest('.image-upload-area');
  if (uploadArea) {
    e.preventDefault();
    uploadArea.style.backgroundColor = 'transparent';
    const file = e.dataTransfer.files[0];
    if (file && file.type.startsWith('image/')) {
      uploadImage(file, uploadArea.closest('.content-block'));
    }
  }
});

// 粘贴图片上传
document.addEventListener('paste', function(e) {
  const activeArea = document.querySelector('.image-upload-area[style*="display: block"]');
  if (activeArea) {
    const items = e.clipboardData.items;
    for (let item of items) {
      if (item.type.startsWith('image/')) {
        const file = item.getAsFile();
        uploadImage(file, activeArea.closest('.content-block'));
        break;
      }
    }
  }
});

// 图片上传函数
function uploadImage(file, block) {
  const formData = new FormData();
  formData.append('image', file);
  
  fetch('upload-image.php', {
    method: 'POST',
    body: formData
  })
  .then(response => response.json())
  .then(data => {
    if (data.success) {
      block.querySelector('.image-path').value = data.path;
      const preview = block.querySelector('.preview-image');
      preview.src = data.path;
      preview.style.display = 'block';
    } else {
      alert(data.message);
    }
  })
  .catch(error => alert('上传失败:' + error.message));
}

三、后端PHP实现

1. 图片上传处理脚本 upload-image.php

<?php
header('Content-Type: application/json');

if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_FILES['image'])) {
  echo json_encode(['success' => false, 'message' => '无效请求']);
  exit;
}

// 配置
$targetDir = 'uploads/';
$allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
$maxSize = 5 * 1024 * 1024; // 5MB

// 创建上传目录
if (!is_dir($targetDir)) {
  mkdir($targetDir, 0755, true);
}

// 校验文件
$file = $_FILES['image'];
if (!in_array($file['type'], $allowedTypes)) {
  echo json_encode(['success' => false, 'message' => '仅支持JPG/PNG/GIF格式']);
  exit;
}
if ($file['size'] > $maxSize) {
  echo json_encode(['success' => false, 'message' => '文件大小不能超过5MB']);
  exit;
}
if ($file['error'] !== UPLOAD_ERR_OK) {
  echo json_encode(['success' => false, 'message' => '文件上传错误']);
  exit;
}

// 生成唯一文件名
$fileName = uniqid('img_') . '.' . pathinfo($file['name'], PATHINFO_EXTENSION);
$targetPath = $targetDir . $fileName;

// 保存文件
if (move_uploaded_file($file['tmp_name'], $targetPath)) {
  echo json_encode(['success' => true, 'path' => $targetPath]);
} else {
  echo json_encode(['success' => false, 'message' => '文件保存失败']);
}
?>

2. 题目保存脚本 save-question.php

<?php
header('Content-Type: application/json');

// 数据库连接(替换为你的配置)
$conn = new mysqli('localhost', 'your_username', 'your_password', 'your_database');
if ($conn->connect_error) {
  echo json_encode(['success' => false, 'message' => '数据库连接失败']);
  exit;
}

// 接收表单数据
$title = trim($_POST['title'] ?? '');
$contentTypes = $_POST['content_type'] ?? [];
$textContents = $_POST['text_content'] ?? [];
$imagePaths = $_POST['image_path'] ?? [];

// 校验标题
if (empty($title)) {
  echo json_encode(['success' => false, 'message' => '题目标题不能为空']);
  exit;
}

// 插入题目主表
$stmt = $conn->prepare("INSERT INTO questions (title, created_at) VALUES (?, NOW())");
$stmt->bind_param("s", $title);
if (!$stmt->execute()) {
  echo json_encode(['success' => false, 'message' => '保存题目失败']);
  $stmt->close();
  $conn->close();
  exit;
}
$questionId = $stmt->insert_id;
$stmt->close();

// 插入内容项
foreach ($contentTypes as $index => $type) {
  $content = ($type === 'text') ? trim($textContents[$index] ?? '') : trim($imagePaths[$index] ?? '');
  if (empty($content)) {
    echo json_encode(['success' => false, 'message' => '内容不能为空']);
    $conn->close();
    exit;
  }
  
  $stmt = $conn->prepare("INSERT INTO question_content (question_id, type, content, sort_order) VALUES (?, ?, ?, ?)");
  $stmt->bind_param("issi", $questionId, $type, $content, $index);
  if (!$stmt->execute()) {
    echo json_encode(['success' => false, 'message' => '保存内容失败']);
    $stmt->close();
    $conn->close();
    exit;
  }
  $stmt->close();
}

$conn->close();
echo json_encode(['success' => true, 'question_id' => $questionId]);
?>

四、内容渲染

从数据库读取题目和内容,按顺序渲染:

<?php
// 数据库连接(替换为你的配置)
$conn = new mysqli('localhost', 'your_username', 'your_password', 'your_database');
$questionId = $_GET['id'] ?? 0;

if (!$questionId) {
  die('无效的题目ID');
}

// 查询题目标题
$stmt = $conn->prepare("SELECT title FROM questions WHERE id = ?");
$stmt->bind_param("i", $questionId);
$stmt->execute();
$result = $stmt->get_result();
$question = $result->fetch_assoc();
$stmt->close();

// 查询内容项(按排序顺序)
$stmt = $conn->prepare("SELECT type, content FROM question_content WHERE question_id = ? ORDER BY sort_order ASC");
$stmt->bind_param("i", $questionId);
$stmt->execute();
$contentItems = $stmt->get_result()->fetch_all(MYSQLI_ASSOC);
$stmt->close();
$conn->close();
?>

<!DOCTYPE html>
<html>
<head>
  <title><?php echo htmlspecialchars($question['title']); ?></title>
</head>
<body>
  <h1><?php echo htmlspecialchars($question['title']); ?></h1>
  <div class="question-content">
    <?php foreach ($contentItems as $item): ?>
      <?php if ($item['type'] === 'text'): ?>
        <p><?php echo nl2br(htmlspecialchars($item['content'])); ?></p>
      <?php else: ?>
        <img src="<?php echo htmlspecialchars($item['content']); ?>" alt="题目图片" style="max-width: 100%; margin: 10px 0;">
      <?php endif; ?>
    <?php endforeach; ?>
  </div>
</body>
</html>

关键注意事项

  1. 安全防护:
    • 图片上传时严格校验文件类型、大小,防止恶意文件上传
    • 输出内容时用htmlspecialchars()转义,避免XSS攻击
    • 数据库操作使用预处理语句,防止SQL注入
  2. 兼容性:
    • 粘贴图片功能需测试主流浏览器(Chrome、Firefox、Edge)
    • 拖放事件需阻止默认行为,避免页面跳转
  3. 优化建议:
    • 可添加图片压缩处理,减少存储占用
    • 内容块可增加上下移动按钮,调整排序顺序

内容的提问来源于stack exchange,提问作者Francois van Ellinckhuyzen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 06:34:56