(161) クリップボード上の画像をWebブラウザに貼りつけて送信する。

投稿者: | 2025年4月29日

468 views

【1】やりたいこと

クリップボード上の画像をファイル出力せず、そのままサーバーへ送りたい。

具体的には、以下の手順で実行したいのだ。

(1) クリップボード上に画像を保存する。
 ↓
(2) Webブラウザ上で画像を貼り付ける。
 ↓
(3) [送信]ボタン押下でサーバーへ送信する。
 ↓
(4) サーバー上で画像ファイルを保存する。

これを実現したい。

【2】やってみる

1) プログラム・ソースコード

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <title>Sample 161</title>
  </head>
  <body>
    <h2>画像を[Ctrl]+[V]で貼り付け → [Upload]ボタンでサーバに送信</h2>
    <canvas id="canvas" style="border:1px solid black;"></canvas><br>
    <button id="uploadBtn">Upload</button>
    <script src="./script.js"></script>
  </body>
</html>
// canvasとcontextを取得
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
let pastedImage = null;

// ペーストイベント検知
document.addEventListener('paste', (event) => {
  const items = (event.clipboardData || event.originalEvent.clipboardData).items;
  for (const item of items) {
    if (item.type.indexOf('image') === -1) {    // 非画像は無視
      continue;
    }
    const file   = item.getAsFile();
    const reader = new FileReader();
    reader.onload = (e) => {
      const img = new Image();
      img.onload = () => {
        // canvasサイズを画像に合わせる
        canvas.width  = img.width;
        canvas.height = img.height;
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.drawImage(img, 0, 0);
        pastedImage = img;
      };
      img.src = e.target.result;
    };
    reader.readAsDataURL(file);
  }
});

// Uploadボタン押下イベント検知 → 画像送信
document.getElementById('uploadBtn').addEventListener('click', () => {
  if (!pastedImage) {
    alert('まず画像を貼り付けてください');
    return;
  }
  canvas.toBlob((blob) => {
    const formData = new FormData();
    formData.append('image', blob, 'pasted.png');
    fetch('upload.php', {
      method: 'POST',
      body: formData
    })
    .then(response => response.text())
    .then(result => {
      alert('アップロード成功: ' + result);
    })
    .catch(error => {
      alert('アップロード失敗: ' + error);
    });
  }, 'image/png');
});
<?php
// 画像が送られてきたかチェック
if (!isset($_FILES['image'])) {
    http_response_code(400);
    echo "No image uploaded.";
    exit;
}

// 保存ディレクトリ
$upload_dir = __DIR__ . '/uploads';

// uploadsフォルダがなければ作成
if (!is_dir($upload_dir)) {
    mkdir($upload_dir, 0755, true);
}

// ファイル名をユニークに生成
$filename = 'img_' . date('Ymd_His') . '_' . bin2hex(random_bytes(5)) . '.png';

// 保存パス
$save_path = $upload_dir . '/' . $filename;

// ファイルを保存
if (move_uploaded_file($_FILES['image']['tmp_name'], $save_path)) {
    echo 'Saved as ' . $filename;
} else {
    http_response_code(500);
    echo 'Failed to save file.';
}
?>

【3】応用:画像を2枚送りたい。

1) プログラム・ソースコード

<!DOCTYPE html>
<html lang="ja">
  <head>
  <meta charset="UTF-8">
    <title>Sample 161(2)</title>
  </head>
  <body>
    <h2>ペースト先を選んで貼り付け → Upload</h2>

    <div>
      <button id="pasteQuestion">質問を貼り付け</button>
      <button id="pasteAnswer">回答を貼り付け</button>
    </div>

    <div style="margin-top:10px;">
      <h3>質問画像</h3>
      <canvas id="questionCanvas" style="border:1px solid black;"></canvas>
    </div>
    <div style="margin-top:10px;">
      <h3>回答画像</h3>
      <canvas id="answerCanvas" style="border:1px solid black;"></canvas>
    </div>

    <div style="margin-top:20px;">
      <button id="uploadBtn">Upload</button>
    </div>

    <script src="./script.js"></script>
  </body>
</html>
const questionCanvas = document.getElementById('questionCanvas');
const answerCanvas   = document.getElementById('answerCanvas');
const ctxQuestion = questionCanvas.getContext('2d');
const ctxAnswer   = answerCanvas.getContext('2d');

// 質問用に貼り付け
document.getElementById('pasteQuestion').addEventListener('click', () => {
  pasteImageToCanvas(questionCanvas, ctxQuestion);
});

// 回答用に貼り付け
document.getElementById('pasteAnswer').addEventListener('click', () => {
  pasteImageToCanvas(answerCanvas, ctxAnswer);
});

// クリップボードから画像を読み取り、指定canvasに貼る
async function pasteImageToCanvas(canvas, ctx) {
  try {
    const items = await navigator.clipboard.read();
    for (const item of items) {
      for (const type of item.types) {
        if (type.startsWith('image/')) {
          const blob = await item.getType(type);
          const img = new Image();
          img.onload = () => {
            canvas.width  = img.width;
            canvas.height = img.height;
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            ctx.drawImage(img, 0, 0);
          };
          img.src = URL.createObjectURL(blob);
          return; // 画像貼り付け完了
        }
      }
    }
    alert('クリップボードに画像がありません');
  } catch (err) {
    alert('クリップボードから画像を読み込めませんでした: ' + err);
  }
}

// Uploadボタン押下イベント
document.getElementById('uploadBtn').addEventListener('click', () => {
  if (questionCanvas.width === 0 || answerCanvas.width === 0) {
    alert('質問と回答、両方の画像を貼り付けてください');
    return;
  }

  const formData = new FormData();

  questionCanvas.toBlob((blob1) => {
    formData.append('question', blob1, 'question.png');

    answerCanvas.toBlob((blob2) => {
      formData.append('answer', blob2, 'answer.png');

      fetch('upload.php', {
        method: 'POST',
        body: formData
      })
      .then(response => response.text())
      .then(result => {
        alert('アップロード成功: ' + result);
        ctxQuestion.clearRect(0, 0, questionCanvas.width, questionCanvas.height);
        ctxAnswer.clearRect(0, 0, answerCanvas.width, answerCanvas.height);
        questionCanvas.width = 0;
        questionCanvas.height = 0;
        answerCanvas.width = 0;
        answerCanvas.height = 0;
      })
      .catch(error => {
        alert('アップロード失敗: ' + error);
      });
    }, 'image/png');
  }, 'image/png');
});
<?php
$upload_dir = __DIR__ . '/uploads';

if (!is_dir($upload_dir)) {
    mkdir($upload_dir, 0755, true);
}

function save_uploaded_image($field_name, $prefix) {
    global $upload_dir;
    if (!isset($_FILES[$field_name])) {
        return false;
    }
    $filename = $prefix . '_' . date('Ymd_His') . '_' . bin2hex(random_bytes(5)) . '.png';
    $save_path = $upload_dir . '/' . $filename;
    if (move_uploaded_file($_FILES[$field_name]['tmp_name'], $save_path)) {
        return $filename;
    }
    return false;
}

$question_saved = save_uploaded_image('question', 'question');
$answer_saved   = save_uploaded_image('answer', 'answer');

if ($question_saved && $answer_saved) {
    echo "Saved: $question_saved and $answer_saved";
} else {
    http_response_code(500);
    echo "Failed to save images.";
}
?>

アクセス数(直近7日): ※試験運用中、BOT除外簡易実装済
  • 2026-02-10: 0回
  • 2026-02-09: 0回
  • 2026-02-08: 0回
  • 2026-02-07: 1回
  • 2026-02-06: 1回
  • 2026-02-05: 0回
  • 2026-02-04: 0回
  • コメントを残す

    メールアドレスが公開されることはありません。 が付いている欄は必須項目です


    日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)