まず、一般的なファイルのアップロードに関する重要な注意事項:キーtype
とname
キーは安全に使用できません。これは、これらがクライアントによって定義されており、サイトに悪意のあるコードを挿入するための潜在的なメカニズムであるためです。../../../../../index.php
ファイル名をに設定した場合、またはMIMEタイプをに設定したimage/gif
が、代わりにPHPファイルをアップロードした場合はどうなるか考えてみてください。
次に、画像のアップロードに関する重要な注意事項:クライアントによってアップロードされた画像データは信頼できません。画像のように見えるものに悪意のあるコードを埋め込むことも可能です。ファイルからピクセルデータをコピーして、新しいピクセルデータを作成する必要があります。これは通常、GD拡張機能を使用して実現されます。
次に、mkdir()
-に3番目の引数があります。この3番目の引数に渡すtrue
と、ディレクトリツリーが再帰的に作成されるため、個別の操作で各レベルを作成する必要はありません。また、(多くのことと同様に)mkdir()
失敗する可能性があることにも注意してください。これが発生した場合は、が返さfalse
れます。これを確認する必要があります。
さて、実際の質問に答えるために(そして前述のセキュリティ問題を少し無視して)、これがあなたのコードを単純化する方法です:
// Configuration
$allowedTypes = array(
"image/gif", "image/jpeg", "image/png", "image/pjpeg"
);
$baseDir = './uploads';
// Check file was uploaded successfully
if ($_FILES['image_name']['error'] > 0) {
exit('Return Code: ' . $_FILES['image_name']['error'] . '<br />');
}
// Check file type
if (!in_array($_FILES["image_name"]["type"], $allowedTypes)) {
exit('Invalid file type: ' . $_FILES['image_name']['type'] . '<br />');
}
// Check/create target directory
list($year, $month, $day) = explode('-', date('y-m-d'));
$targetDir = $baseDir . '/' . $year . '/' . $month . '/' . $day;
if (!is_dir($targetDir)) {
if (!mkdir($targetDir, 0644, true)) {
exit('Failed to create destination directory<br />');
}
}
// Store the uploaded file permanently
$targetPath = $targetDir . '/' . .$_FILES['image_name']['name'];
if (!move_uploaded_file($_FILES['image_name']['tmp_name'], $targetPath)) {
exit('Failed to move temporary file<br />');
}
しかし、私はこれをしません。
ファイルのアップロードは非常に一般的なタスクであり、私が使用する一般的なコードは次のようになります。複雑そうですね。それは、ファイルのアップロードの処理が簡単ではないためです。ただし、その複雑さは、上記で概説したセキュリティ上の懸念に対処するための優れた簡単な方法を提供することです。画像のサポートが組み込まれており、サイズを変更してクリーンで簡単な方法で変更するオプションが含まれています。
コードでどのように使用するかを見てみましょう。
$baseDir = './uploads';
// Very simple autoloader for demo purposes
spl_autoload_register(function($class) {
require strtolower(basename($class)).'.php';
});
// When you instantiate this the $_FILES superglobal is destroyed
// You must access all uploaded files via this API from this point onwards
$uploadManager = new \Upload\Manager;
// Fetches a FileCollection associated with the named form control
$control = $uploadManager->getControl('image_name');
// getControl returns NULL if there are no files associated with that name
if (!isset($control)) {
exit('No file was uploaded in the image_name control');
}
// Check/create target directory
// You still need to do this, it's not magic ;-)
list($year, $month, $day) = explode('-', date('y-m-d'));
$targetDir = $baseDir . '/' . $year . '/' . $month . '/' . $day;
if (!is_dir($targetDir)) {
if (!mkdir($targetDir, 0644, true)) {
exit('Failed to create destination directory');
}
}
// This also handles multiple uploads in a single control, so we need to loop
foreach ($control as $image) {
// You need to determine a file name to use, most likely not from user
// input. This is a high-entropy low collision-risk random approach.
$targetFile = $targetDir . '/' . uniquid('upload-', true);
try {
$image->save($targetFile, true, IMAGETYPE_ORIGINAL);
} catch (\Exception $e) {
exit("Oh noes! Something went badly wrong: ".$e->getMessage());
}
}
これは、前に概説したセキュリティ上の懸念に対処するために、バックグラウンドで多くのことを行います。画像が有効で認識されたタイプであることを自動的に検出し、保存されたファイルにも正しいファイル拡張子を適用します。