- 投稿日:
adiaryの改造でやったのでメモがてら
環境
Env | Ver |
---|---|
Microsoft Edge | 120.0.2210.144 |
PHP | 8.2.10 |
サンプルコード
FORM形式での送信
Web標準でサポートされているため実装が非常に容易。特に理由がなければこれでいい
HTMLコード
<textarea onpaste="handlePasteForm(event)"></textarea>
JSコード
fetch()
のヘッダ指定がないが、 fetch()
を使う場合はFormDataを使うと勝手に生えるので気にしなくていい。他にもXMLHttpRequest.send()
やNavigator.sendBeacon()
でも生えるらしい。
今回試したEdgeではバウンダリー文字列もちゃんと生えていた。
/** @param {ClipboardEvent} e */
const handlePasteForm = (e) => {
if (e.clipboardData.files.length) {
const pasteFile = e.clipboardData.files[0];
const form = new FormData();
form.append('image', pasteFile);
fetch('./test.php', {
method: 'POST',
body: form
});
} else {
// 何もしない
}
};
PHPコード
Content-Type
がmultipart/form-data
の時だけ中身が入ってくる
参考:https://www.php.net/manual/ja/reserved.variables.files.php
<?php
$file_name = $_FILES['image']['name'];
move_uploaded_file($_FILES['image']['tmp_name'], './'. $file_name);
>
$_FILES
の中身はvar_dumpした限りこんな感じだった
array(1) {
["image"]=>
array(6) {
["name"]=>
string(9) "image.png"
["full_path"]=>
string(9) "image.png"
["type"]=>
string(9) "image/png"
["tmp_name"]=>
string(29) "C:\env\msys64\tmp\php3B90.tmp"
["error"]=>
int(0)
["size"]=>
int(3988)
}
}
JSON形式での送信
Web標準ではないので、これといったやり方もなく、正直面倒だが、どうしてもJSONで送らないといけないときに。
HTMLコード
<textarea onpaste="handlePasteJson(event)"></textarea>
JSコード
HTTPではバイナリを送ることができないため、Base64にデコードして送る。Base64デコードに含まれる情報はファイルバイナリのみ。もしFORM送信のようにファイル名も送りたければ、別途送ってやる必要がある。
/** @param {File} file */
const encodeBase64 = (file) => {
return new Promise ((resolve, reject) => {
const fr = new FileReader();
fr.onload = () => {
const data = fr.result.split(',')[1];
resolve(fr.result.split(',')[1]);
};
fr.onerror = (ev) => {
reject(ev);
};
fr.readAsDataURL(file);
});
};
/** @param {ClipboardEvent} e */
const handlePasteJson = async (e) => {
if (e.clipboardData.files.length) {
const pasteFile = e.clipboardData.files[0];
const base64File = await encodeBase64(pasteFile);
fetch('./test.php', {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({ image: base64File })
});
} else {
// 何もしない
}
};
PHPコード
JSONリクエストを取るときはfile_get_contents('php://input')
を利用する。$_POST
はContent-Type
がapplication/x-www-form-urlencoded
ないしmultipart/form-data
の時しか取れないからだ。つまりFORMリクエスト以外はfile_get_contents('php://input')
で取ると覚えておけばよいだろう。
参考:https://www.php.net/manual/ja/wrappers.php.php#wrappers.php.input
以下のコードはかなりいい加減なので余りアテにしてはいけない(特に拡張子取得の下りがガバガバすぎる)が、取り合えずpngかjpegかgifが送られてきた場合は動いてくれる筈だ。
<?php
// 連想配列にするため、json_decode()の第二引数をtrueにする(指定しないとstdClassになる
$json = json_decode(file_get_contents('php://input'), true);
// Base64エンコードを解除する
$file = base64_decode($json['image']);
// 一時ファイルに落とす
file_put_contents('json_upload', $file);
// 拡張子判別
$mime = mime_content_type('json_upload');
// 拡張子取得
$ext = explode('/', $mime);
// 拡張子付きにリネーム
rename('json_upload', 'image.'. $ext[1]);
参考
JSONでファイルを投げる方式での実装状況については以下の記事が参考になる。
WebAPI でファイルをアップロードする方法アレコレ #API - Qiita
一気通貫で動くサンプルコード
今までの実装が全部動かせるサンプル
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if(preg_match('/^multipart\/form-data;/', $_SERVER['CONTENT_TYPE'])) {
// form
$file_name = $_FILES['image']['name'];
move_uploaded_file($_FILES['image']['tmp_name'], './'. $file_name);
} else {
// json
$json = json_decode(file_get_contents('php://input'), true);
$file = base64_decode($json['image']);
file_put_contents('json_upload', $file);
$mime = mime_content_type('json_upload');
$ext = explode('/', $mime);
rename('json_upload', 'image.'. $ext[1]);
}
} else {
?>
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta http-equiv='X-UA-Compatible' content='IE=edge'>
<title>Upload test</title>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<script>
/** @param {File} file */
const encodeBase64 = (file) => {
return new Promise ((resolve, reject) => {
const fr = new FileReader();
fr.onload = () => {
const data = fr.result.split(',')[1];
resolve(fr.result.split(',')[1]);
};
fr.onerror = (ev) => {
reject(ev);
};
fr.readAsDataURL(file);
});
};
/** @param {ClipboardEvent} e */
const handlePasteForm = (e) => {
if (e.clipboardData.files.length) {
const pasteFile = e.clipboardData.files[0];
const form = new FormData();
form.append('image', pasteFile);
fetch('./test.php', {
method: 'POST',
body: form
});
} else {
// 何もしない
}
};
/** @param {ClipboardEvent} e */
const handlePasteJson = async (e) => {
if (e.clipboardData.files.length) {
const pasteFile = e.clipboardData.files[0];
const base64File = await encodeBase64(pasteFile);
fetch('./test.php', {
method: 'POST',
headers: {
'content-type': 'application/json'
},
body: JSON.stringify({ image: base64File })
});
} else {
// 何もしない
}
};
</script>
</head>
<body>
<div>Form:<textarea onpaste="handlePasteForm(event)"></textarea></div>
<div>Json:<textarea onpaste="handlePasteJson(event)"></textarea></div>
</body>
</html>
<?php
}
?>
- 投稿日:
- 素のHTMLにJSを埋め込んでイベントコールバックの引数を取る方法
- イベントコールバックの引数に
event
を指定する- 例:
onclick="test(event)"
- 例:
- イベントコールバックの引数に
サンプルコード
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>Example of DOM Event callback arguments</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
const test = (ev) => {
console.log(ev);
};
</script>
</head>
<body>
<p onclick="test(event)">aaa</p>
</body>
</html>
- 投稿日:
maxlength
- iOS Safariでは効かない
onInput()
でstring.slice(0, maxlength)
するとIMEの挙動が可笑しくなるtype="tel"
など日本語が入力できない場合であれば有効
- オートコンプリートやコピペ入力での字切れなどもあるため、根本的に使わないことが望ましい
type="number"
- iOS Safariでは期待した動作にはならない
- IMEが有効になり、全角入力が発生する
- 使うなら
type="tel"
を使い、JSで数字以外の入力を弾くのが無難 - 恐らく普及ブラウザの全てで半角入力を強制出来、スマホなどではNumPadが出てくる
- アルファベットやハイフンなどの記号も打てるので必要に応じた入力制御が必要
- 投稿日:
JavaScriptで添付ファイルを拾うのと、ファイルを落とす処理のサンプルコード
<html>
<head>
<meta charset='utf-8'>
<title>js file up/dl example</title>
<script>
window.onload = () => {
const fr = new FileReader();
const el = document.getElementById("test");
fr.onload = (ev) => {
const dl = document.createElement('a');
dl.setAttribute('href', ev.target.result);
dl.setAttribute('download', el.files[0].name);
dl.style.display = 'none';
document.body.appendChild(dl);
dl.click();
document.body.removeChild(dl);
};
el.onchange = () => {
fr.readAsDataURL(el.files[0]);
};
};
</script>
</head>
<body>
<input type="file" id="test"></input>
</body>
</html>