【PHP】アップロード・ダウンロード機能
記事更新日:2022-09-04
呼び出し側
<?php
$array = array (
// ページの基本情報
'base' => array (
'page_title' => 'タイトル', // ページタイトル
'directory_path' => '../uploads/', // ファイルの格納場所
'after_page' => '../xx/xx.php', // アップ後の表示ページ
),
// ダウンロードリスト(indexをゼロ始まりで指定:ファイルの数だけindexを増やす)
'download_lists' => array (
'0' => array (
"text" => "Excelファイル", // 画面に表示させる文字
"file_name" => "aa.xlsx",
"text_class" => "c-icon--excel", // テキストに個別にクラス名を付ける場合
"name" => "download_file1", // for,name,idにつける名前
"accept" => ".xlsx", // 許可する拡張子(複数の場合はコンマ区切りで書く)
"type" => "submit", // inputタイプ
"value" => "ダウンロード", // ボタンに表示する文字
"rename" => false, // ダウンロード時にリネームする場合はtrue(=ファイル名の末尾に日付追加)
),
'1' => array (
"text" => "HTMLファイル",
"file_name" => "bb.html",
"text_class" => "c-icon--html",
"name" => "download_file2",
"accept" => ".html",
"type" => "submit",
"value" => "ダウンロード",
"rename" => true,
),
),
// アップロードリスト
'upload_lists' => array (
"text" => array (
"Excelファイル" => "aa.xlsx",
"HTMLファイル" => "bb.html",
"PDFファイル" => "cc.pdf",
),
"text_class" => "",
"accept" => ".xlsx,.html,.pdf", // 複数の場合:カンマ区切り
"type" => "file",
"value" => "アップロード",
),
);
// 配列から変数へ格納
$page_title = $array['base']['page_title'];
$directory_path = $array['base']['directory_path'];
$after_page = $array['base']['after_page'];
$download_lists = $array['download_lists'];
$upload_lists = $array['upload_lists'];
// ダウンロードボタンが押された時の処理
usa_processing_download($directory_path, $download_lists);
// アップロードボタンが押された時の処理
usa_processing_upload($directory_path, $after_page);
// フロント描画
usa_make_maintenance_page($page_title, $download_lists, $upload_lists);
ダウンロードボタンが押された時の処理
<?php
/**
* 機能 ダウンロードボタンが押された時の処理
* @param array $directory_path ダウンロードするディレクトリパス
* @param array $download_lists 必要データが入ってる配列
*/
function usa_processing_download($directory_path, $download_lists) {
// 配列の要素数
$count = count($download_lists);
// 配列の要素数分、ループ
for ($i = 0 ; $i < $count; $i++) {
$fullpath = $directory_path . $download_lists[$i]['file_name'];
$post_name = $download_lists[$i]['name'];
$my_mime_type = $download_lists[$i]['accept'];
$rename = $download_lists[$i]['rename'];
if ( isset( $_POST[$post_name] ) ) {
usa_download($fullpath, $my_mime_type, $rename);
}
};
}
アップロードボタンが押された時の処理
<?php
/**
* 機能 アップロードボタンが押された時の処理
* @param string $directory_path アップ先のディレクトリパス
* @param string $after_page アップ後に表示する該当の掲載ページ(デフォルトはandroidページ)
*/
function usa_processing_upload($directory_path, $after_page="") {
$up_btn_name = "_upload";
$up_name = "userfile";
// ボタンが押されたら関数へフルパスを渡す
if ( isset( $_POST[$up_btn_name] ) ) {
usa_upload($directory_path, $up_name, $after_page);
};
}
サーバーから指定ファイルをダウンロードする
<?php
/**
* 機能 サーバーから指定ファイルをダウンロードする
* @param string $fullpath アクセスするフルパス
* @param string $my_mime_type mimeタイプを指定したい場合(省略可)
* @param bool $ng_rename リネームNGの場合はtrueにする(デフォルト値はfalse)
*/
function usa_download($fullpath, $my_mime_type = null, $rename) {
// ファイルの読み込みができない時はエラー
if (!is_readable($fullpath)) {
die($fullpath);
}
// Content-Type用にMIMEタイプ設定(第2引数がない場合は自動判定)
$mimeType = (isset($my_mime_type)) ? $my_mime_type : (new finfo(FILEINFO_MIME_TYPE))->file($fullpath);
// MIMEタイプが得られない時は、未知のファイルを示すapplication/octet-streamとする
if (!preg_match('/\A\S+?\/\S+/', $mimeType)) {
$mimeType = 'application/octet-stream';
}
// ※もしも上記が使えない場合はこっちで
// if (empty($mime_type)) {
// $mime_type = "application/octet-stream";
// }
// ファイル名のリネーム
$file_name = basename($fullpath);
// renameがtrueなら、ファイル名の末尾に秒付きの日付を追加する
if ($rename) {
$file_name = usa_rename_filename_before_extension($file_name);
}
header('Content-Type: ' . $mimeType); // Content-Typeにmimeタイプを設定
header('X-Content-Type-Options: nosniff'); // ウェブブラウザが独自にMIMEタイプを判定する処理を抑止する
header('Content-Length: ' . filesize($fullpath)); // ダウンロードファイルのサイズ
header('Content-Disposition: attachment; filename="' . $file_name . '"'); // ダウンロード時のファイル名
header('Connection: close'); // keep-aliveの無効化
// 出力バッファリングの無効化 ※メモリの大量消費を回避するため
while (ob_get_level()) {
ob_end_clean();
}
// 出力
readfile($fullpath);
// 必ず終了処理をする
exit;
};
サーバーへ選択されたファイルをアップロードし、結果メッセージを表示する
<?php
/**
* 機能: サーバーへ、選択されたファイルをアップロードし、結果メッセージを表示する
* @param string $upload_dir アップする場所
* @param string $arr_name 格納されている配列の名前(name属性が名前になる)
* @param string $after_page アップ後にデータが表示されるページ(確認用に開く)
*
*/
function usa_upload($upload_dir, $arr_name, $after_page) {
// リファラの取得
$referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
$src = parse_url($referer, PHP_URL_PATH);
$result = '';
$up_name = '';
$flag_succeed = false;
$rename_file ='';
if (isset($_POST['_upload'])) {
$count_files = count($_FILES[$arr_name]['name']);
// アップ数が1つの場合
if ($count_files === 1) {
// ファイル名取得にはbasenameを使う:ファイルシステムトラバーサル攻撃からの防護のため
$base_name = basename($_FILES[$arr_name]['name']);
// アップ名をフルパスで定義
$up_fullpath = makeFullPathUploadFile($upload_dir, $base_name);
// ファイルを仮置き場(=tmp)から、指定場所($up_fullpath)へ移動できたら成功
if (move_uploaded_file($_FILES[$arr_name]['tmp_name'], $up_fullpath)) {
$flag_succeed = true;
// up_name = アップされた名前
$up_name = $_FILES[$arr_name]['name'];
// リネームした場合は、表示用のタグを生成し、up_nameを書き換える
makeRenameTag($base_name, $up_name, $rename_file);
// フロント表示用の成功タグの生成
$result = makeSucceedTag($result, $up_name);
// 指定場所へ移動できなかったらエラー表示
} else {
$up_name = $_FILES[$arr_name]['name'];
$err = errorMassage($_FILES[$arr_name]['error']);
// フロント表示用のエラータグの生成
$result = makeErrTag($result, $up_name, $err);
};
// 複数の場合
} else {
foreach ($_FILES[$arr_name]['tmp_name'] as $i => $tmp_name) {
// ファイル名取得にはbasenameを使う:ファイルシステムトラバーサル攻撃からの防護のため
$base_name = basename($_FILES[$arr_name]['name'][$i]);
// アップ名をフルパスで定義
$up_fullpath = makeFullPathUploadFile($upload_dir, $base_name);
// ファイルを仮置き場(=tmp)から、指定場所へ移動できたら成功
if (move_uploaded_file($tmp_name, $up_fullpath)) {
$flag_succeed = true;
// up_name = アップされた名前
$up_name = $_FILES[$arr_name]['name'][$i];
// リネームした場合は、表示用のタグを生成し、up_nameを書き換える
makeRenameTag($base_name, $up_name, $rename_file);
// フロント表示用の成功タグの生成
$result = makeSucceedTag($result, $up_name);
// 指定場所へ移動できなかったらエラー表示
} else {
$up_name = $_FILES[$arr_name]['name'][$i];
$err = errorMassage($_FILES[$arr_name]['error'][$i]);
// フロント表示用のエラータグの生成
$result = makeErrTag($result, $up_name, $err);
};
}
}
} else {
$result = 'ボタンのクリック時に通信エラーがありました。恐れ入りますが、もう一度やり直してください';
};
// メッセージボックス(サマリー)のクラス名設定
$class_summary = "p-summary-box c-mb--40";
// 成功の場合はクラス名にsuccessを追加し、該当の掲載ページを表示する(画面がかぶらないように高さは低めに指定)
if ( $flag_succeed ) {
$class_summary = $class_summary . " success";
?>
<script> window.open('<?php echo $after_page; ?>', '_blank', 'width=800, Height=300'); </script>
<?php
}
?>
<img src="../assets/img/dog-face.png" class="c-confetti-dog" alt="喜んでる犬のイラスト">
<article class="p-msg-box" id="js-msg-box">
<article class="<?php echo $class_summary;?>" id="js-summary-box">
<article class="p-summary-box__inner">
<div class="c-text-box c-fs--big">
<p class="text">【 <?php echo $count_files;?>ファイルの結果 】</p>
<p class="c-fs--big result"><?php echo $result;?></p>
<?php if ($rename_file !== ''): ?>
<p class="rename text c-fs--normal"><span style="display: inline;">(リネーム)</span><?php echo $rename_file; ?></p>
<?php endif; ?>
<?php if ( $flag_succeed ): ?>
<p class="text bless">ダウンロード成功!</p>
<?php endif;?>
<button class="c-button c-gap-inner--normal c-bg-color--btn-secondary c-btn-shape--rounded c-shadow--blur c-fs--big" id="js-back-button">元のページに戻る</button>
</div>
</article>
</article>
<article class="p-msg-box__detail c-fs--small">
<p class="c-fs--normal">結果表示</p>
<span>---↓ここから↓---</span>
<span>送信元:<?php echo $src;?></span>
<span>送信先:<?php echo $upload_dir;?></span>
<?php if ($rename_file !== ''): ?>
<span style="display: inline;">リネーム:</span><?php echo $rename_file; ?>
<?php endif; ?>
<span>↓↓アップロード結果</span>
<pre><?php var_dump($_FILES[$arr_name]);?></pre>
<span>---↑ここまで↑---</span>
</article>
</article>
<?php
header('Content-Type: text/html; charset=UTF-8');
include('../templates/footer/read-js/after-upload.php');
};
エラーメッセージ
<?php
function errorMassage($error_num) {
switch ($error_num) {
case UPLOAD_ERR_OK:
// 0
break;
case 1:
// UPLOAD_ERR_INT_SIZE
$result = 'エラー:ファイルサイズが大きすぎます';
break;
case UPLOAD_ERR_FORM_SIZE:
// 2
$result = 'エラー:ファイルサイズが大きすぎます';
break;
case UPLOAD_ERR_PARTIAL:
// 3
$result = '通信エラー:恐れ入りますが、もう一度やり直してください';
break;
case UPLOAD_ERR_NO_FILE:
// 4
$result = 'システムエラー:選択ファイルを読み取れませんでした。恐れ入りますが、もう一度やり直してください';
break;
case UPLOAD_ERR_NO_TMP_DIR:
// 6
$result = 'システムエラー:選択ファイルを読み取れませんでした。恐れ入りますが、もう一度やり直してください';
break;
default:
$result = 'システムエラー:恐れ入りますが、もう一度やり直してください';
break;
}
return $result;
};
アップロードファイル名をフルパスで定義する
<?php
/**
* アップロードファイル名をフルパスで定義する
*/
function makeFullPathUploadFile($upload_dir, &$base_name) {
// 日付入りであれば、日付無しに書き換え
$base_name = isDateInBaseName($base_name);
// フルパスで定義
$up_fullpath = $upload_dir . $base_name;
return $up_fullpath;
}
アップしたファイル名に日付部分が含まれる場合はリネームする
<?php
/**
* アップしたファイル名に日付部分が含まれる場合はリネームする
*/
function isDateInBaseName($base_name) {
$base_name_len = mb_strlen($base_name);
// '_yyyymmddhhmmss'部分の文字数
$date_num = 15;
$bar_num = '';
$date_str = '';
// base_name_lenが日付分よりも大きい場合のみ判定(拡張子分の文字数(最低4文字)も足す)
if ($base_name_len > $date_num + 4) {
$dot_num = mb_strrpos($base_name, "."); // 拡張子.の位置
$bar_num = $dot_num - $date_num; // アンダーバーの位置
};
// $bar_numが設定されている&その位置にアンダーバーがあれば、アンダーバー以降を抽出
if ($bar_num !== '' && $base_name[$bar_num] === '_') {
$date_str = mb_substr($base_name, $bar_num + 1, $date_num - 1); //'yyyymmddhhmmss'部分を抽出(アンダーバー無し)
};
// $date_strに値が入っていて、その値がすべて数字なら、$base_nameから$date_strを削除する
if ($date_str !== '' && is_numeric($date_str)) {
$date_str = mb_substr($base_name, $bar_num, $date_num); //'_yyyymmddhhmmss'部分を抽出(アンダーバー付きに変換)
$base_name = str_replace($date_str, '', $base_name);
};
return $base_name;
};
ファイル名をリネームしたとき、表示用のタグを生成しup_nameの値を変更する
<?php
/**
* 機能: ファイル名をリネームしたとき、表示用のタグを生成し、up_nameの値を変更する
*/
function makeRenameTag($base_name, &$up_name, &$rename_file) {
// アップされた名前が、最初の名前と違う場合
if ( $up_name !== $base_name ) {
$rename_file = $rename_file . '<span>' . $up_name . ' → ' . $base_name . '</span>';
$up_name = $base_name;
}
}
最終結果を表示するタグの生成
<?php
/**
* 機能: 最終結果を表示するタグの生成
*/
function makeSucceedTag($result, $up_name) {
$result = $result . '<span class="c-bg-color--yellow">' . $up_name . ":" . "アップしました</span>";
return $result;
}
エラー時、エラー表示のためのタグを生成
<?php
/**
* 機能: エラー時、エラー表示のためのタグを生成
*/
function makeErrTag($result, $up_name, $err) {
$result = $result . '<span class="error c-bg-color--red">' . $up_name . ':' . $err . '</span>';
return $result;
}
文字列の後ろに日付(秒付き)を追加する
<?php
// **
// 機能 文字列の後ろに日付(秒付き)を追加する
// @param string $string 対象の文字列
//
// 結果 string_yymmdd_hhiiss
// **
function usa_add_date_after_string($string) {
$adding = $string . '_' . date("Ymd") . '_' . date("His");
return $adding;
};
拡張子の前に文字列を追加する
<?php
// **
// 機能 拡張子の前に文字列を追加する
// @param string $filename 元のファイル名(拡張子付き)
// @param string $adding 拡張子の直前に追加したい文字列 ※省略された場合は日付(秒付き)を自動付与する
//
// 備考 strrpos関数:文字列中に、ある部分文字列が最後に現れる場所($pos)を取得する
// →「$posの前半 + 追加文字 + $posの後半」でリネームする
// **
function usa_rename_filename_before_extension($filename, $adding = "") {
if ($adding == "") {
$adding = '_' . date("YmdHis");
}
$pos = strrpos($filename, '.'); // .が最後に現れる位置
if ($pos) {
return (substr($filename, 0, $pos) . $adding . substr($filename, $pos));
} else {
return ($filename . $adding);
}
};
2022-09-04
編集後記:
この記事の内容がベストではないかもしれません。