以前は、収集した記事と写真を保存する収集ツールに取り組んでいました。記事の内容はデータベースに保存され、写真は最初に写真サーバーにアップロードする必要があり、その後、写真のアドレスが返されます。記事の画像アドレスを置き換えます
ここで問題が発生します: すべては正常に収集できますが、ローカル テストは正常に行われ、画像は正常にアップロードできますが、実稼働環境には画像がありません。ステップバイステップで、データがそこにあることを確認しましたが、残念ながら、本番環境では画像が正常にアップロードされませんでした
数日間苦労した後、コードを段階的に読み、デバッグし、Baidu を使用した後、最終的に、それは本当に大きな落とし穴でした。
curl post を使用して画像サーバーにアップロードする
PHP の cURL は、CURL_POSTFIELDS に (文字列の代わりに) 連想配列を渡すことによってマルチパート/フォームデータ POST リクエストの生成をサポートしています。
従来、PHP の cURL は、cURL が読み取りおよびアップロードする配列データで「@+完全なファイル パス」構文を使用することによるファイルの添付をサポートしていました。これは、コマンド ラインから cURL プログラムを直接呼び出すための構文と一致しています:
curl_setopt(ch, CURLOPT_POSTFIELDS, array( 'file' => '@'.realpath('image.png'),));equals$ curl -F "file=@/absolute/path/to/image.png" <url>
ただし、PHP では、ファイルを指すための新しい CURLFile クラスが 5.5 から導入されました。 CURLFile クラスは、マルチパート/フォームデータ データに表示される MIME タイプ、ファイル名などの追加情報を詳細に定義することもできます。 PHP では、CURLFile を使用して古い @ 構文を置き換えることをお勧めします。
curl_setopt(ch, CURLOPT_POSTFIELDS, [ 'file' => new CURLFile(realpath('image.png')),]);
PHP 5.5 では、CURL_SAFE_UPLOAD オプションも導入されています。これにより、PHP の cURL モジュールが古い @ 構文を拒否し、CURLFile 形式のファイルのみを受け入れるように強制できます。デフォルト値は、5.5 の場合は false、5.6 の場合は true です。
しかし、落とし穴は次のとおりです。@ 構文は 5.5 で非推奨となり、5.6 では直接削除されました (ElorException が生成されます: ファイルのアップロードでの @filename API の使用は非推奨です。代わりに CURLFile クラスを使用してください)。
PHP 5.6 以降の場合、CURL_SAFE_UPLOAD を手動で false に設定することは無意味です。これは、文字通り「false に設定すると安全でない古いメソッドが有効になる」とは理解されていません。古いメソッドは廃止された構文として完全に存在しなくなりました。 PHP 5.6+ == CURLFile のみ。幻想を抱かないでください。
私のデプロイ環境は 5.4 (@syntax のみ) ですが、開発環境は 5.6 (CURLFile のみ) です。どちらも、両方がサポートする移行バージョンである 5.5 には焦点を当てていないため、環境判断を備えた 2 セットのコードを作成する必要があります。
さて問題は…
私はこの種の環境判定コードを見たことがあります:
うわーこの種のコードに対する私の評価はただ一言: クソ。
この判断は、典型的な マジックナンバーの罠 に陥ります。バージョン番号はコード内に不可解に表示されますが、PHP マニュアルや更新履歴を長時間確認しないと、作成者がどの機能変更に引っかかっているのかを理解するのは困難です。
コードはそのルーツに戻る必要があります。実際のニーズは、従来の @ 構文に戻らずに、最初に CURLFile を使用することです。コードは次のとおりです:
if (version_compare(phpversion(), '5.4.0') >= 0)
信頼性の観点から、古い @ 構文を許容するか禁止するかを PHP に明確に指示するために CURL_SAFE_UPLOAD の値を指定することをお勧めします。 CURLOPT_SAFE_UPLOAD 定数自体は、PHP の以前のバージョンには存在しない可能性があることに注意してください。
if (class_exists('\CURLFile')) { $field = array('fieldname' => new \CURLFile(realpath($filepath)));} else { $field = array('fieldname' => '@' . realpath($filepath));}
curl_setopt() のシングルショットであるか、curl_setopt_array() のバッチであるかにかかわらず、cURL オプションは常に 1 つずつ設定すると有効になり、オプションを設定すると、後続のオプションを設定するときの cURL の動作にすぐに影響します。
たとえば、CURLOPT_SAFE_UPLOAD は CURLOPT_POSTFIELDS の動作に関連しています。 CURLOPT_POSTFIELDS が最初に設定され、次に CURLOPT_SAFE_UPLOAD が設定された場合、後者の制約は有効になりません。なぜなら、前者を設定すると、cURL はデータの実際の読み取りと処理をすでに完了しているからです。
cURL にはこの落とし穴があるオプションがたくさんあるので注意してください。幸いなことに、この種の「依存関係」には選択肢は多くなく、仕組みも複雑ではないため、簡単に扱うことができます。私の方法は、最初にすべてのオプションをバッチで設定し、次にcurl_setopt()を使用してcurl_exec()の直前まで一度にCURLOPT_POSTFIELDSを設定することです。
実際、curl_setopt_array() で使用される配列では、後ろの CURLOPT_POSTFIELDS の位置も信頼できることが保証されています。 PHP の連想配列は順序が保証されています また、curl_setopt_array() 内の実行順序は最初から最後まで順になっている必要があると想定できるため、安心してください。
私のアプローチは、コードのパフォーマンスにさらなる保険を追加し、将来の間違いを防ぐための順序の重要性を強調することです。
PHP バージョン 5.2 以下には名前空間がありません。コード内でスペース区切り文字を使用すると、パーサー エラーが発生します。 PHP 5.2 を扱うことを考えるのは実際には簡単です。名前空間を放棄するだけです。
注意する必要があるのは、名前空間を備えた PHP 5.3 以降です。 CURLFile を呼び出す場合でも、class_exists() を使用して CURLFile の存在を確認する場合でも、名前空間でラップされるときにコードがクラッシュしないように、CURLFile を作成してトップレベルのスペースを明確に指定することをお勧めします。
さて、この穴はかなり深いので、飛び出したら共有します
(上記の解決策は Web サイトからの転載です。記事を見つけてくれてありがとう!)