CURLFileを使ったらエラーになった話
CURLでファイルアップロードをする際、フォームとして送る時はCURLFileを使うのが一般的となりました。
少し前までは「@」を使ったアップロードだったのですが、セキュリティ的な問題もあり、今では使用することはほぼないと思います。
今回はそんなCURLFileで発生したエラーについて書いてみようと思います。
はじめに
今回はCURLを使ったコマンドラインのファイルアップロードに関する記事です。
PHPでCURLを使用する方法や詳細なオプションなどは公式を参照してください。
cURL / PHP Manual
CURLFile / PHP Manual
環境
今回は下記のような環境になってます。
【テスト環境】
CentOS 7
PHP Version 7.3.10
curl 7.61.1
【本番環境】
Windows Server
PHP Version 7.3.2
curl 7.61.1
ご覧の通り、ほとんど同じ環境になっています。
CURLFileでファイルアップロード
では、CURLでForm形式のファイルアップロードを行うプログラムを書いていきましょう。
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 57 58 59 60 |
$dir = dirname(__FILE__).DIRECTORY_SEPARATOR; $fileName = 'テスト画像.jpg'; $filePath = $dir.fileName; $mimeType = 'image/jpeg'; // CURLFileオブジェクトを作成 $file = new CURLFile($filePath, $mimeType); $url = '送信先URL'; $header = [ 'Content-Type: multipart/form-data', ]; $files = [ 'file' => $file, ]; $curl = curl_init(); // URL設定 curl_setopt($curl, CURLOPT_URL, $url); // リクエストメソッド curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'POST'); curl_setopt($curl, CURLOPT_POST, true); // 証明書検証をしない curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false); // 結果を文字列で取得 curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); // リクエストにヘッダーを含める curl_setopt($curl, CURLOPT_HTTPHEADER, $header); // データフィールド curl_setopt($curl, CURLOPT_POSTFIELDS, $files); // タイムアウト時間を設定 curl_setopt($curl, CURLOPT_TIMEOUT, 10); // Locationヘッダーの内容をたどる curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true); // ヘッダーを結果に含める curl_setopt($curl, CURLOPT_HEADER, true); $response = curl_exec($curl); // CURLの情報とヘッダー、ボディーを取得 $curlInfo = curl_getinfo($curl); $responseHeader = explode("\r\n", trim(substr($response, 0, $curlInfo['header_size']))); $responseBody = substr($response, $curlInfo['header_size']); // エラーチェック $errorNo = curl_errno($curl); $errorMsg = curl_error($curl); if(CURLE_OK !== $errorNo) { // エラー発生 } curl_close($curl); |
この時のポイントは CURLFileにはフルパスを指定する というところです。
この転送方法の場合、受ける方のプログラムでは $_FILES で受け取ることができます。
エラーに遭遇する
テスト環境でアップロードができることを確認し、本番に入れたところ $curlInfo[‘http_code’] が 0 で返ってくるエラーに遭遇しました。
開発当初はPHPバージョンやCURLのバージョンが異なっていたため、テスト環境をほぼ同じに合わせてみましたが、結果は変わりませんでした。
また curl_errno で取得したエラーコードは 26(CURLE_READ_ERROR) が返ってきているにもかかわらず curl_error は何のエラー文字列も返って来ないという奇妙なエラーでした。
通常なら CURLE_READ_ERROR はファイルパーミッションやPOSTの最大ファイルサイズによるものとも考えましたが、ここも特に問題が無く、途方に暮れてしまいました。
今回のエラーの原因
みなさんはここまでの情報で何が原因かわかりましたか?
今回の原因はPHPでもApacheでもなく OSの違いによるもの でした。
ここでプログラムを見直してみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
$dir = dirname(__FILE__).DIRECTORY_SEPARATOR; $fileName = 'テスト画像.jpg'; $filePath = $dir.fileName; $mimeType = 'image/jpeg'; // CURLFileオブジェクトを作成 $file = new CURLFile($filePath, $mimeType); $url = '送信先URL'; $header = [ 'Content-Type: multipart/form-data', ]; $files = [ 'file' => $file, ]; $curl = curl_init(); // 割愛.... |
はい、マーカーがついている行が問題となったところです。
お気づきの方もいると思いますが、今回のファイル名は 「日本語(マルチバイト文字)」 が使用されています。
日本語(マルチバイト文字)の扱いはOSによって異なります。
今回のエラーは CURLE_READ_ERROR つまり 日本語ファイルが読み込めない という意味だったんです。
最近のPHPでのデフォルトエンコードは UTF-8 となっていることが多いですが、Windowsのファイルシステムでは未だ ShiftJIS です。
この違いからファイル名が文字化けしてしまい、読み込めないというものでした。
これを解決させるためには、下記のような感じで文字コードを変換してあげればOKでした。
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 |
$dir = dirname(__FILE__).DIRECTORY_SEPARATOR; $fileName = 'テスト画像.jpg'; $filePath = $dir.fileName; $mimeType = 'image/jpeg'; // ファイルパスやファイル名を変換する $filePath = mb_convert_encoding($filePath, 'SJIS-win'); // CURLFileオブジェクトを作成 $file = new CURLFile($filePath, $mimeType); $url = '送信先URL'; $header = [ 'Content-Type: multipart/form-data', ]; $files = [ 'file' => $file, ]; $curl = curl_init(); // 割愛.... |
これにより、ディレクトリ名に日本語があってもエンコードの変換を行ってファイルがアップできるようになりました。
さいごに
今回のこの情報は、国内外の質問サイトでも全く書いておらず、多大な時間を取られたエラーでした。
今後は日本語ファイル名やディレクトリ名の時は、特に注意して取り扱おうと思いました。
- おすすめ記事
POPULAR
のえる
Full-stack Developer