プロが教えるわが家の防犯対策術!

ファイルのアップロード機能を作成していて、アップロードする場所にカメラの画像を配置しているのですが、ファイルをアップロードするとカメラ画像の下に画像や動画やpdfが表示されてしまうため、
カメラ画像の上にアップロードファイルを表示したいと考えております。
画像、動画、PDFで分岐するコードを書きたい場合、どのように書けばよいのでしょうか?

<style>
/* 通常時 */
form > div { display: flex; }
.contents-selector-button {
position: relative;
margin: .3em;
}
.contents-selector-button label img {/*カメラ画像に対してのcss*/
width: 200px; height: 100px;
}
.contents-selector-button input { display: none; }

/* 画像、動画、PDF表示時のレイアウト */
.contents-selector-button.contents-on label { visibility: hidden; }
.contents-selector-button.contents-on .viewer {
position: absolute;
top:0; left: 0;
width: 100%; height: 100px;
overflow: hidden;
}
</style>
</head>


<body>
<form method="post">
<div>
<div class="contents-selector-button">
<label>
<div><img src="img/camera.jpg"></div>
<input type="file" accept=".png, .jpg, .jpeg, .pdf, .mp4">
</label>
<input type="hidden">
<button type="button">clear</button>
<div class="viewer"></div>
</div>
</div>
</form>


<script>
(()=>{
const
fileTypes = ['image/jpeg','image/png','video/mp4','application/pdf'],
wrap = document.querySelector('form > div'),
P = e => e.closest('.contents-selector-button'),
V = e => P(e).querySelector('.viewer'),
C = (e, s) => P(e).classList[s]('contents-on');

for(let i = 0; i<3; i++)
wrap.appendChild(wrap.firstElementChild.cloneNode(1));

/* クリアボタン */
wrap.addEventListener('click', (e, t = e.target) => {
if(t.nodeName != 'BUTTON' || t.type != 'button') return;
V(t).innerHTML = '';
C(t, 'remove');
});

/* 画像、動画、PDF登録 */
wrap.addEventListener('change', (e, t = e.target) => {
if(t.nodeName != 'INPUT' || t.type != 'file') return;
if(t.files.length == 0 || !fileTypes.includes(t.files[0].type)) return;


/* ここから分岐させたい */
/* 画像の場合 */
wrap.addEventListener('change', (e, t = e.target) => {
if(t.nodeName != 'INPUT' || t.type != 'file') return;
if(t.files.length == 0 || !fileTypes.includes(t.files[0].type)) return;
const img = document.createElement('img');
img.src = URL.createObjectURL(t.files[0]);
img.style.height = '100px';
V(t).appendChild(img);
C(t, 'add');
});
})();


/* 動画の場合 */
wrap.addEventListener('change', (e, t = e.target) => {
if(t.nodeName != 'INPUT' || t.type != 'file') return;
if(t.files.length == 0 || !fileTypes.includes(t.files[0].type)) return;
const img = document.createElement('video');
video.src = URL.createObjectURL(t.files[0]);
video.style.height = '100px';
V(t).appendChild(video);
C(t, 'add');
});
})();


/* PDFの場合 */
wrap.addEventListener('change', (e, t = e.target) => {
if(t.nodeName != 'INPUT' || t.type != 'file') return;
if(t.files.length == 0 || !fileTypes.includes(t.files[0].type)) return;
const img = document.createElement('pdf');
video.src = URL.createObjectURL(t.files[0]);
video.style.height = '100px';
V(t).appendChild(pdf);
C(t, 'add');
});
})();

</script>

A 回答 (4件)

No3です。



>修正いただいたコードに別途コードを追加すべきでしょうか?
>PHP側でチェックをファイルタイプのチェックは行っております。
全体像がよくわかりません。
どこにもそのような説明は無いので・・・

スクリプト側でのチェックは、あくまで簡易的なものと考えてください。
スクリプトでいろいろチェックしても、悪意のあるユーザがくぐり抜けることは可能ですので、サーバ側できちんと再チェックする必要があります。
(同じチェック内容であっても省略はできません)

とは言っても、No3のマークアップ例では、送信できるようにはなっていませんけれど・・
    • good
    • 0
この回答へのお礼

回答ありがとうございます、全体像についての説明が欠けておりました申し訳ありません。
アップロードファイルをbase64データ変換を行いPHPでバイナリーデータを確認しております。ファイル名はセキュリティ上信用できないためバイナリーデータから拡張子を判定して該当しない場合はファイルエラーになるようにしているのでPHP側ではチェックは問題ない気がします。

コードが長いためsample.phpにPHP側でチェックコードを載せさせていただきました。

※該当コード
https://wandbox.org/permlink/7jBbvXUm0QbRlXj5

お礼日時:2023/06/25 21:45

No2です。



>~~にclass="hideItems"は必要でしょうか?
HTML文法上必要とされるnameやidは省略しない方が良いでしょう。
それ以外に関しては、作成者の好み的なところもあります。
主には、CSSやスクリプトから参照する都合で設定しているものが多いと思います。
(私は、メモ帳に手打ちなので可能なら省略しちゃいますけれど・・)

ご提示の、INPUT要素は常に非表示の要素なので、そのクラスの有無で何が違うのかよくわかりません。(CSSがどうなっているのかも不明ですし・・)
もしも、付与してもしなくても変わりがないのなら不要とも言えますし、それでも好みで設定したからと言って、別に問題は生じないと思います。

なお、No2にも記しましたが、.createObjectURL()で紐づけを行った場合は、revokeObjectURL() で解放してあげる必要があるようです。
とは言え、動画の場合は解放のタイミングがちょっと難しそうですね。


以下は、画像等の読み込み方法を変えてみた例です。ご参考までに。
<参考ページ>
https://developer.mozilla.org/ja/docs/Web/API/Fi …

※ 送信の必要はないようなので、formタグははずしてあります。
 (他の不要そうなdivもはずしてありあます)
※ PDFのデフォルト表示がブラウザによって異なるようですが、特に対処はしていません。

<!DOCTYPE HTML>
<html lang="ja">
<head><title>Sample</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

<style>
/* 通常時のレイアウト */
#wrapper { display: flex; }
.image-selector-button {
position: relative;
margin: .3em;
}
.image-selector-button label img {
display: block;
width: 200px; height: 100px;
}
.image-selector-button input { display: none; }

/* 画像等表示時のレイアウト */
.image-selector-button.image-on .viewer {
position: absolute;
top:0; left: 0;
width: 100%; height: 100px;
background-color: white;
overflow: hidden;
}
</style>
</head>
<body>
<div id="wrapper">
<div class="image-selector-button">
<label>
<img src="./img/photo00.jpg">
<input type="file" accept=".png, .jpg, .jpeg, .pdf, .mp4">
</label>
<button type="button">clear</button>
<div class="viewer"></div>
</div>
</div>


<script>
(()=>{
const
fileTypes = [
{ type: 'image/png', tag: 'img' }, { type: 'image/jpeg', tag: 'img' },
{ type: 'video/mp4', tag: 'video' }, { type: 'application/pdf', tag: 'iframe' }
],
wrap = document.getElementById('wrapper'),
P = e => e.closest('.image-selector-button'),
V = e => P(e).querySelector('.viewer'),
C = (e, s) => P(e).classList[s]('image-on');

for(let i = 0; i<2; i++)
wrap.appendChild(wrap.firstElementChild.cloneNode(1));

/* クリアボタン */
wrap.addEventListener('click', (e, t = e.target) => {
if(t.nodeName != 'BUTTON' || t.type != 'button') return;
P(t).querySelector('input').value = '';
V(t).innerHTML = '';
C(t, 'remove');
});

/* 画像登録 */
wrap.addEventListener('change', (e, t = e.target) => {
if(t.nodeName != 'INPUT' || t.type != 'file' || t.files.length == 0) return;
const file = t.files[0], f = fileTypes.filter( e => e.type == file.type)[0];
if( !f ) return;
const obj = document.createElement( f.tag );
const rdr = new FileReader();
rdr.onload = e => { obj.src = e.target.result; };
rdr.readAsDataURL(file);
if( f.tag == 'video') obj.controls = true;
obj.style.height = '100px';
V(t).appendChild(obj);
C(t, 'add');
});
})();
</script>
</body>
</html>
    • good
    • 1
この回答へのお礼

うーん・・・

Q.HTML文法上必要とされるnameやidは省略しない方が良いでしょう。
ご提示の、INPUT要素は常に非表示の要素なので、そのクラスの有無で何が違うのかよくわかりません。(CSSがどうなっているのかも不明ですし・・)
もしも、付与してもしなくても変わりがないのなら不要とも言えますし、それでも好みで設定したからと言って、別に問題は生じないと思います。

A.アドバイスありがとうございます、慣れてないうちは書くようにしてみます。

どうしてもお聞きしておきたいことがあるのですが、修正いただいたコードに別途コードを追加すべきでしょうか?
こちらの質問をする以前に別の方に、Javascriptでもセキュリティ対策コードを書いた方がよいとアドバイスを頂きました、
修正いただいたコードを参考に考えていきたいのですがウイルス対策が充分なのか心配です。MIME typeで分岐しているため該当しないファイルはキャンセルしているので問題ないと思うのですが…
PHP側でチェックをファイルタイプのチェックは行っております。

※アドバイスを頂いた文
Javascriptでファイルアップロードの際にアップロードされたファイルの拡張子や内容を厳密に検証し、信頼できるファイル形式のみを許可して、実行可能なファイルや危険なファイルを拒否してください。

PHPでアップロードされたファイルの拡張子を正規化し、意図しないファイルタイプのマイムタイプを避けるために、サーバーサイドで適切なファイルタイプのチェックを行ってください。

お礼日時:2023/06/25 01:22

No1です。



>どうまとめればよいのか自分でも分からないのですが、~~
>につなげるにはどのように書けばよいのでしょうか?
No1の回答は「ちゃんと処理できるスクリプトがあるのなら、そちらを利用すれば良いのでは」ということです。
無理に混在させることを考える必要はありません。
(処理全体が異なるので、そちらの方が難しいです)
お手持ちのものが、ご希望の処理になっているのなら、全部を入れ替えてしまえばよいのではないでしょうか?
(回答のスクリプトは、File APIを良く知らないので、テスト用に簡易的に作成したものですので・・)

ただし、使用するスクリプトに合うように、HTMLの id やクラス名は設定しておく必要があります。



ちなみに、
>f(t.nodeName != 'INPUT' || t.type != 'file') return;
>if(t.files.length == 0 || !fileTypes.includes(t.files[0].type)) return;
では、.png、.jpegのチェックもまとめて記述してありますので、その後に記述しても画像以外は既にはじかれてしまっています。
if(t.nodeName != 'INPUT' || t.type != 'file' || t.files.length == 0) return;
とでもしておいて、その後でMIMEタイプをチェックすれば、分岐は可能になると思います。
    • good
    • 0
この回答へのお礼

うーん・・・

アドバイスありがとうございます。回答者さまのコードを参考にコードは考えてみたのですが、changeImg[i].classList.add('hideItems');の部分が引っ掛かります。<input type="file" id="inputFile" class="hideItems" accept=".png, .jpg, .jpeg, .pdf, .mp4">にclass="hideItems"は必要でしょうか?

※最新コード
https://wandbox.org/permlink/JALzQaRCAjmXFa6y

お礼日時:2023/06/21 19:55

こんにちは



前回回答者です。
https://oshiete.goo.ne.jp/qa/13495417.html

前回のご質問は、表示レイアウトに関するご質問だと認識しているのですが・・・
その際に説明や実物でも伝わらないようでしたので、動作する簡単なスクリプトを付けて回答したただけのものです。
ですので、スクリプトに関しては、もともとお使いのものが機能するのなら、そちらを利用なされば宜しいのではないでしょうか?
前回の説明にも書きましたように、レイアウトの変更は、クラス設定だけで可能な仕組みになっています。


>画像、動画、PDFで分岐するコードを書きたい場合、
>どのように書けばよいのでしょうか?
INPUT要素に入力された、input.file.typeを取得すると、MIMEタイプが返されますので、それで分岐すれば良いでしょう。
例えば、.pngであれば ”image/png” が返されます。(文字列で取得できます)

なお、前回は動作確認のためだけなので省略していますが、.createObjectURL()でオブジェクトURLを作成するとメモリが拘束されるので、revokeObjectURL() で解放してあげる必要があるようですのでご注意ください。
https://note.affi-sapo-sv.com/js-create-revoke-o …
    • good
    • 1
この回答へのお礼

回答ありがとうございます、どうまとめればよいのか自分でも分からないのですが、if(t.nodeName != 'INPUT' || t.type != 'file') return;
if(t.files.length == 0 || !fileTypes.includes(t.files[0].type)) return;
につなげるにはどのように書けばよいのでしょうか?
簡略して書くことに慣れていないためアドバイスを頂きたいです。よろしくお願い致します。

<script>
/* 画像、動画、PDF登録 */
wrap.addEventListener('change', (e, t = e.target) => {
const fileType = fileDate.type;//MIMEタイプで分岐
let newElement;// 新しい要素を取得
if (['image/jpeg', "image/png"].includes(fileType)) {
newElement = document.createElement('img');
} else if (fileType === 'video/mp4') {
newElement = document.createElement('video');
newElement.controls = true;
} else if (fileType === 'application/pdf') {
newElement = document.createElement("iframe");
} else {
return alert("対象外のファイルです");
}

</script>

お礼日時:2023/06/21 01:35

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!