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回