laravel5.1で、複数ファイルをアップロードしてダウンロードするやり方を調べてみた(日本語ファイル名対応済)
写真やPDFなど、複数のフィールドに複数のファイルを格納したい!という仕様なので、調べてみた(今回はPDFオンリー)
1, テーブルのカラムは、ファイル名をセミコロン区切りで入れるから、ただのテキストにする
1 2 |
$table->text('file1')->nullable()->comment('複数PDFファイル格納可'); $table->text('file2')->nullable()->comment('複数PDFファイル格納可'); |
2, 新規作成画面と更新画面を作る。超大切&よく忘れるのは、form属性に’enctype’=>’multipart/form-data’を指定しないと、複数ファイルを送信してもlaravel側で認識できないから超注意だ!!!
1 2 3 4 |
// 新規作成 {!! Form::open(['url' => 'user', 'method' => 'POST', 'enctype'=>'multipart/form-data']) !!} // 更新 {!! Form::model($user, ['route'=>['user.update', $user->id], 'method' => 'put', 'enctype'=>'multipart/form-data']) !!} |
普通の入力フォームの項目と違うのは、要素名の後ろに[]をつけて配列型にする。属性に’multiple’=>trueを追加して、複数ファイルアップロード許可する。
1 2 3 4 |
// 新規作成 {!! Form::file('file1[]', array('multiple'=>true)) !!} // 更新は、既にアップロード済なので、ファイルへのリンクを表示する {!! $file_links[0] !!}{!! Form::file('file1[]', array('multiple'=>true)) !!} |
3, 更新画面を表示する処理
1 2 3 4 5 6 7 8 9 |
public function edit($id) { $user = User::findOrFail($id); //削除チェックボックス付きで、アップロード済PDFをファイルリストを取得する $file_links = user::show_uploaded_files($id, true); return view('users.edit', compact('user', 'file_links', 'sv_pulldown_data', 'show_password')); } |
4, $file_links[](アップロード済みファイル)を表示すメインの処理は、Modelクラス(User.php)に記述する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
// アップロード済のファイルリスト(リンク付)を表示する public static function show_uploaded_files($id, $edit=false) { $user = user::findOrFail($id); // アップロードファイル2種類(PDF複数ファイル) $colum_values = [$user->file1, $user->file2 ]; $colum_names = ['file1','file2']; $file_links = array_fill(0, count($colum_names), ''); // 複数PDFファイルを格納できるカラムの数だけループ for($i=0; $i<count($colum_names); $i++){ // アップロードされた複数PDFファイルへのリンクを生成 $upload_path = '/user/upload/' . $id . '/' . $colum_names[$i]; $arr_files = explode(";", $colum_values[$i]); foreach($arr_files as $file){ if(empty($file)){ //ファイルが存在しないなら、何も表示しない }else{ // 半角スペース区切りで、ファイル名とリンクを表示する。 $file_links[$i] .= " <a href=" . url($upload_path .'/'. rawurlencode($file)) .">" . rawurldecode($file) . "</a> "; // 編集画面だったら、削除チェックボックスを追加する。 if($edit){ $file_links[$i] .= "<input type='checkbox' name='". $colum_names[$i] ."_delete[]' value=". $file."> 削除 <br>"; } } } } return $file_links; } |
5, 更新ボタンを押された時の処理。まずはUserController.phpで処理を受け付ける(更新の場合)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public function update(Request $request, $id){ // テーブルからレコードを取得 $this->user = user::findOrFail($id); // アップロードされたファイル&削除を反映させる(レコード反映は、この関数の最後) $data = User::update_uploaded_files( $request, $id); // レコードを更新 $this->user->fill($data); $this->user->save(); return redirect()->to('/user/'.$id); } |
6, メインの処理は、Modelクラス(User.php)に記述する。削除用チェックボックスにチェックがあったら削除。ファイルが送られてきたら/public/upload/user/{$id}/file1/フォルダに保存される。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
// use Illuminate\Http\Request;を忘れずに! // (新規作成・更新で共用) 削除されたり、新しいファイルをアップロードされたらpublic/uploadに格納。DBテーブルにはファイル名のみ格納 public static function update_uploaded_files(Request $request, $id){ // アップロードファイルの種類だけ記述(PDF複数ファイル) $colum_names = ['file1','file2']; $data = $request->all(); // アップロードファイルの更新処理 for($i=0; $i<count($colum_names); $i++){ $upload_path = public_path() . '/upload/user/' . $id . '/' . $colum_names[$i] . '/'; // 削除のチェックボックスが入力されていたら if(!empty($data[$colum_names[$i] . '_delete'])){ // アップロード済みファイルを削除する foreach($data[ $colum_names[$i] . '_delete'] as $delete_file_path){ \File::delete($upload_path . $delete_file_path); } // DBにないフィールドなので削除しておく unset($data[$colum_names[$i] . '_delete']); } // 1つ以上ファイルがアップロードされてきたら格納 // use Illuminate\Http\Request;を忘れずに! $all_file_names = ""; if($request->hasFile($colum_names[$i])){ foreach($request->file($colum_names[$i]) as $file) { $filename = rawurlencode($file->getClientOriginalName()); // アップロード先フォルダを生成(作成済なら何もしない) \File::makeDirectory($upload_path, $mode = 0777, true, true); // 実際のファイルコピー $file->move($upload_path,$filename); // 区切り文字は、セミコロン $all_file_names = $all_file_names. $filename. ";"; } } //Webサーバ上のファイルリストを取得 $server_filelist = ''; $list = \File::files($upload_path); // ファイル名のみ連結して記述 foreach($list as $name){ $server_filelist .= basename($name) . ";" ; } // 最後のセミコロンを削除してからDB格納 $data[$colum_names[$i]] = substr($server_filelist, 0, -1); } // 呼び出し元にレコード更新してもらう return $data; } |
7, 新規作成も更新とほとんど同じだが、INSERTするまで$idが確定しないので、INSERT->UPDATEという順番になる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
public function store(Request $request) { $data = $request->all(); // 複数ファイルのアップロードは配列なので、そのままだとテキストフィールドに格納できない。以下のエラーになる // ErrorException in helpers.php line 686: preg_replace(): Parameter mismatch, pattern is a string while replacement is an array unset($data['file1']); unset($data['file2']); // $idを確定させるために、INSERTする $this->user->fill($data); $this->user->save(); $id = $this->user->id; // 一度INSERTすると$idが確定するので、アップロードされたファイルを格納する $data_update = User::update_uploaded_files( $request,$id); $this->user = user::findOrFail($id); $this->user->fill($data_update); $this->user->save(); return redirect()->to('/user/' . $id); } |
8, ファイル名をクリックされた時の処理を作る。英数字ファイル名なら
1 |
<a href="filename">filename</a> |
で楽なのだが、
a, 日本語ファイル名のままリンクを貼るとエラーになるのでNG!
b, URLエンコードすると表示されるが、ダウンロード時にURLエンコードされたままなのでダメ!
c, HTTPレスポンスヘッダにファイル名を入れれば良いらしい。
d, つまりルーティングが必要。
route.php
1 2 |
// アップロードしたPDFファイルのダウンロード Route::get('/user/upload/{id}/{file_kind}/{file_name}', 'UserController@uploadfile'); |
UserContoroller.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// 日本語ファイル名がアップロードされた時に、ダウンロードした時も日本語に直す処理 public function uploadfile($id, $file_kind, $file_name) { // ダウンロード対象ファイル $upload_filepath = public_path() . '/upload/user/' . $id . '/' . $file_kind . '/' . rawurlencode($file_name); // レスポンスヘッダで、ファイル名やMIMEを指定する! $headers = array( 'Content-Type' => "application/pdf", // どんなファイルか分からない時は、application/octet-streamを指定する 'Content-Disposition' => 'attachment; filename="' . $file_name . '"' ); return \Response::download($upload_filepath,$file_name, $headers ); } |