업로드된 파일 다루기
CodeIgniter는 PHP의 $_FILES 배열을 직접 사용하는 것보다 폼을 통해 업로드된 파일을 훨씬 간단하고 안전하게 다룰 수 있게 해줍니다. 이는 File 클래스를 확장하므로 해당 클래스의 모든 기능을 사용할 수 있습니다.
참고
이것은 CodeIgniter 3의 파일 업로드 클래스와 다릅니다. 업로드된 파일에 대한 원시 인터페이스를 몇 가지 기능과 함께 제공합니다.
파일 업로드 폼 튜토리얼
파일 업로드는 다음과 같은 일반적인 과정을 포함합니다:
사용자가 파일을 선택하고 업로드할 수 있는 업로드 폼이 표시됩니다.
폼이 제출되면 지정한 목적지에 파일이 업로드됩니다.
이 과정에서 설정한 기본 설정에 따라 파일이 업로드 허용 여부를 검증합니다.
업로드가 완료되면 사용자에게 성공 메시지가 표시됩니다.
이 과정을 설명하기 위한 간략한 튜토리얼이 있습니다. 이후에 참조 정보를 확인할 수 있습니다.
업로드 폼 만들기
텍스트 편집기를 사용하여 upload_form.php라는 폼을 만듭니다. 그 안에 이 코드를 넣고 app/Views 디렉터리에 저장합니다:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Upload Form</title>
</head>
<body>
<?php foreach ($errors as $error): ?>
<li><?= esc($error) ?></li>
<?php endforeach ?>
<?= form_open_multipart('upload/upload') ?>
<input type="file" name="userfile" size="20">
<br><br>
<input type="submit" value="upload">
</form>
</body>
</html>
폼 헬퍼를 사용하여 폼 여는 태그를 생성하고 있음을 알 수 있습니다. 파일 업로드는 멀티파트 폼이 필요하므로 헬퍼가 적절한 구문을 생성해 줍니다.
$errors 변수도 있음을 알 수 있습니다. 사용자가 잘못된 작업을 한 경우 오류 메시지를 표시하기 위해 사용됩니다.
성공 페이지
텍스트 편집기를 사용하여 upload_success.php라는 폼을 만듭니다. 그 안에 이 코드를 넣고 app/Views 디렉터리에 저장합니다:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Upload Form</title>
</head>
<body>
<h3>Your file was successfully uploaded!</h3>
<ul>
<li>name: <?= esc($uploaded_fileinfo->getBasename()) ?></li>
<li>size: <?= esc($uploaded_fileinfo->getSizeByUnit('kb')) ?> KB</li>
<li>extension: <?= esc($uploaded_fileinfo->guessExtension()) ?></li>
</ul>
<p><?= anchor('upload', 'Upload Another File!') ?></p>
</body>
</html>
컨트롤러
텍스트 편집기를 사용하여 Upload.php라는 컨트롤러를 만듭니다. 그 안에 이 코드를 넣고 app/Controllers 디렉터리에 저장합니다:
<?php
namespace App\Controllers;
use CodeIgniter\Files\File;
class Upload extends BaseController
{
protected $helpers = ['form'];
public function index()
{
return view('upload_form', ['errors' => []]);
}
public function upload()
{
$validationRule = [
'userfile' => [
'label' => 'Image File',
'rules' => [
'uploaded[userfile]',
'is_image[userfile]',
'mime_in[userfile,image/jpg,image/jpeg,image/gif,image/png,image/webp]',
'max_size[userfile,100]',
'max_dims[userfile,1024,768]',
],
],
];
if (! $this->validateData([], $validationRule)) {
$data = ['errors' => $this->validator->getErrors()];
return view('upload_form', $data);
}
$img = $this->request->getFile('userfile');
if (! $img->hasMoved()) {
$filepath = WRITEPATH . 'uploads/' . $img->store();
$data = ['uploaded_fileinfo' => new File($filepath)];
return view('upload_success', $data);
}
$data = ['errors' => 'The file has already been moved.'];
return view('upload_form', $data);
}
}
업로드된 파일의 유효성 검사에는 파일 업로드 규칙만 사용할 수 있습니다.
따라서 required 규칙도 사용할 수 없으므로, 파일이 필수인 경우 대신 uploaded 규칙을 사용하십시오.
$this->validateData()의 첫 번째 인수로 빈 배열([])이 전달된다는 점에 주목하십시오. 파일 유효성 검사 규칙이 업로드된 파일의 데이터를 Request 객체에서 직접 가져오기 때문입니다.
폼에 파일 업로드 외의 다른 필드가 있다면 필드 데이터를 첫 번째 인수로 전달합니다.
라우트
텍스트 편집기를 사용하여 app/Config/Routes.php를 엽니다. 그 안에 다음 두 라우트를 추가합니다:
<?php
// ...
/*
* --------------------------------------------------------------------
* Route Definitions
* --------------------------------------------------------------------
*/
// We get a performance increase by specifying the default
// route since we don't have to scan directories.
$routes->get('/', 'Home::index');
$routes->get('upload', 'Upload::index'); // Add this line.
$routes->post('upload/upload', 'Upload::upload'); // Add this line.
// ...
업로드 디렉터리
업로드된 파일은 writable/uploads/ 디렉터리에 저장됩니다.
사용해 보세요!
폼을 사용해 보려면 다음과 유사한 URL로 사이트를 방문합니다:
example.com/index.php/upload/
업로드 폼이 표시될 것입니다. 이미지 파일(jpg, gif, png, 또는 webp)을 업로드해 보세요. 컨트롤러의 경로가 올바르면 작동해야 합니다.
파일 접근
모든 파일
파일을 업로드하면 PHP에서 $_FILES 슈퍼전역 변수를 통해 기본적으로 접근할 수 있습니다. 이 배열은 여러 파일을 동시에 업로드할 때 몇 가지 주요 단점이 있으며, 많은 개발자가 인식하지 못하는 잠재적인 보안 취약점이 있습니다. CodeIgniter는 공통 인터페이스 뒤에서 파일 사용을 표준화하여 이러한 두 가지 상황을 모두 해결합니다.
파일은 현재 IncomingRequest 인스턴스를 통해 접근합니다. 이 요청과 함께 업로드된 모든 파일을 가져오려면 getFiles()를 사용합니다. CodeIgniter\HTTP\Files\UploadedFile의 인스턴스로 표현되는 파일 배열을 반환합니다:
<?php
$files = $this->request->getFiles();
물론 파일 입력의 이름을 지정하는 방법은 여러 가지가 있으며, 가장 단순한 방법이 아니면 예상치 못한 결과가 발생할 수 있습니다. 배열은 예상하는 방식으로 반환됩니다. 가장 단순한 사용법으로 단일 파일은 다음과 같이 제출될 수 있습니다:
<input type="file" name="avatar">
이는 다음과 같은 단순 배열을 반환합니다:
[
'avatar' => // UploadedFile instance,
];
참고
UploadedFile 인스턴스는 $_FILES에 대응합니다. 사용자가 제출 버튼만 클릭하고 파일을 업로드하지 않더라도 인스턴스는 존재합니다. UploadedFile의 isValid() 메서드로 파일이 실제로 업로드되었는지 확인할 수 있습니다. 파일 검증을 참조하십시오.
이름에 배열 표기법을 사용한 경우 입력은 다음과 같이 표시됩니다:
<input type="file" name="my-form[details][avatar]">
getFiles()가 반환하는 배열은 다음과 같습니다:
[
'my-form' => [
'details' => [
'avatar' => // UploadedFile instance
],
],
]
경우에 따라 업로드할 파일 배열을 지정할 수 있습니다:
Upload an avatar: <input type="file" name="my-form[details][avatars][]">
Upload an avatar: <input type="file" name="my-form[details][avatars][]">
이 경우 반환되는 파일 배열은 다음과 같습니다:
[
'my-form' => [
'details' => [
'avatar' => [
0 => // UploadedFile instance,
1 => // UploadedFile instance,
],
],
],
]
단일 파일
단일 파일에만 접근하면 되는 경우 getFile()을 사용하여 파일 인스턴스를 직접 가져올 수 있습니다. CodeIgniter\HTTP\Files\UploadedFile의 인스턴스를 반환합니다:
가장 단순한 사용법
가장 단순한 사용법으로 단일 파일은 다음과 같이 제출될 수 있습니다:
<input type="file" name="userfile">
이는 다음과 같은 단순 파일 인스턴스를 반환합니다:
<?php
$file = $this->request->getFile('userfile');
배열 표기법
이름에 배열 표기법을 사용한 경우 입력은 다음과 같이 표시됩니다:
<input type="file" name="my-form[details][avatar]">
파일 인스턴스를 가져오려면:
<?php
$file = $this->request->getFile('my-form.details.avatar');
여러 파일
<input type="file" name="images[]" multiple>
컨트롤러에서:
<?php
if ($imagefile = $this->request->getFiles()) {
foreach ($imagefile['images'] as $img) {
if ($img->isValid() && ! $img->hasMoved()) {
$newName = $img->getRandomName();
$img->move(WRITEPATH . 'uploads', $newName);
}
}
}
여기서 images는 폼 필드 이름으로부터의 반복입니다.
같은 이름의 파일이 여러 개인 경우 getFile()을 사용하여 각 파일을 개별적으로 가져올 수 있습니다.
컨트롤러에서:
<?php
$file1 = $this->request->getFile('images.0');
$file2 = $this->request->getFile('images.1');
같은 이름의 업로드된 파일 배열을 가져오려면 getFileMultiple()을 사용하는 것이 더 편리할 수 있습니다:
<?php
$files = $this->request->getFileMultiple('images');
다른 예시:
Upload an avatar: <input type="file" name="my-form[details][avatars][]">
Upload an avatar: <input type="file" name="my-form[details][avatars][]">
컨트롤러에서:
<?php
$file1 = $this->request->getFile('my-form.details.avatars.0');
$file2 = $this->request->getFile('my-form.details.avatars.1');
참고
getFiles()를 사용하는 것이 더 적절합니다.
파일 다루기
UploadedFile 인스턴스를 가져온 후에는 파일에 대한 정보를 안전하게 얻을 수 있으며 파일을 새 위치로 이동할 수도 있습니다.
파일 검증
isValid() 메서드를 호출하여 파일이 오류 없이 HTTP를 통해 실제로 업로드되었는지 확인할 수 있습니다:
<?php
if (! $file->isValid()) {
throw new \RuntimeException($file->getErrorString() . '(' . $file->getError() . ')');
}
이 예시에서 볼 수 있듯이, 파일에 업로드 오류가 있으면 getError() 및 getErrorString() 메서드로 오류 코드(정수)와 오류 메시지를 가져올 수 있습니다. 이 메서드를 통해 다음 오류를 발견할 수 있습니다:
파일이
upload_max_filesizeini 지시자를 초과합니다.파일이 폼에 정의된 업로드 제한을 초과합니다.
파일이 일부만 업로드되었습니다.
업로드된 파일이 없습니다.
파일을 디스크에 쓸 수 없습니다.
파일을 업로드할 수 없습니다: 임시 디렉터리가 없습니다.
PHP 확장에 의해 파일 업로드가 중단되었습니다.
파일 이름
getName()
getName() 메서드로 클라이언트가 제공한 원래 파일 이름을 가져올 수 있습니다. 이는 일반적으로 클라이언트가 보낸 파일 이름이므로 신뢰해서는 안 됩니다. 파일이 이동된 경우 이동된 파일의 최종 이름을 반환합니다:
<?php
$name = $file->getName();
getClientName()
파일이 이동된 경우에도 클라이언트가 보낸 업로드된 파일의 원래 이름을 항상 반환합니다:
<?php
$originalName = $file->getClientName();
getTempName()
업로드 중 생성된 임시 파일의 전체 경로를 가져오려면 getTempName() 메서드를 사용할 수 있습니다:
<?php
$tempfile = $file->getTempName();
기타 파일 정보
getClientExtension()
업로드된 파일 이름을 기반으로 원래 파일 확장자를 반환합니다:
<?php
$ext = $file->getClientExtension();
경고
이것은 신뢰할 수 없는 출처입니다. 신뢰할 수 있는 버전은 대신 guessExtension()을 사용하십시오.
getClientMimeType()
클라이언트가 제공한 파일의 MIME 타입을 반환합니다. 이것은 신뢰할 수 없는 값입니다. 신뢰할 수 있는 버전은 대신 getMimeType()을 사용하십시오:
<?php
$type = $file->getClientMimeType();
echo $type; // image/png
getClientPath()
Added in version 4.4.0.
클라이언트가 디렉터리 업로드를 통해 파일을 업로드한 경우 업로드된 파일의 webkit 상대 경로를 반환합니다. PHP 8.1 미만 버전에서는 null을 반환합니다.
<?php
$clientPath = $file->getClientPath();
echo $clientPath; // dir/file.txt, or dir/sub_dir/file.txt
파일 이동
원래 파일 이름으로
각 파일은 적절하게 이름 붙여진 move() 메서드를 사용하여 새 위치로 이동할 수 있습니다. 첫 번째 파라미터로 파일을 이동할 디렉터리를 받습니다:
<?php
$file->move(WRITEPATH . 'uploads');
기본적으로 원래 파일 이름이 사용됩니다.
새 파일 이름으로
두 번째 파라미터로 전달하여 새 파일 이름을 지정할 수 있습니다:
<?php
$newName = $file->getRandomName();
$file->move(WRITEPATH . 'uploads', $newName);
기존 파일 덮어쓰기
기본적으로 대상 파일이 이미 존재하면 새 파일 이름이 사용됩니다. 예를 들어, 디렉터리에 image_name.jpg가 이미 존재하면 파일 이름은 자동으로 image_name_1.jpg가 됩니다.
세 번째 파라미터로 true를 전달하여 기존 파일을 덮어쓸 수 있습니다:
<?php
$file->move(WRITEPATH . 'uploads', null, true);
파일 이동 여부 확인
파일이 이동되면 임시 파일이 삭제됩니다. hasMoved() 메서드로 파일이 이미 이동되었는지 확인할 수 있으며, 불리언을 반환합니다:
<?php
if ($file->isValid() && ! $file->hasMoved()) {
$file->move($path);
}
이동 실패 시
업로드된 파일 이동은 다음과 같은 여러 상황에서 HTTPException과 함께 실패할 수 있습니다:
파일이 이미 이동된 경우
파일이 성공적으로 업로드되지 않은 경우
파일 이동 작업이 실패한 경우 (예: 권한 부족)
파일 저장
각 파일은 적절하게 이름 붙여진 store() 메서드를 사용하여 새 위치로 이동할 수 있습니다.
가장 단순한 사용법으로 단일 파일은 다음과 같이 제출될 수 있습니다:
<input type="file" name="userfile">
기본적으로 업로드 파일은 writable/uploads 디렉터리에 저장됩니다. YYYYMMDD 폴더와 무작위 파일 이름이 생성됩니다. 파일 경로를 반환합니다:
<?php
$path = $this->request->getFile('userfile')->store();
첫 번째 파라미터로 파일을 이동할 디렉터리를 지정할 수 있습니다. 두 번째 파라미터로 전달하여 새 파일 이름을 지정합니다:
<?php
$path = $this->request->getFile('userfile')->store('head_img/', 'user_name.jpg');
업로드된 파일 이동은 다음과 같은 여러 상황에서 HTTPException과 함께 실패할 수 있습니다:
파일이 이미 이동된 경우
파일이 성공적으로 업로드되지 않은 경우
파일 이동 작업이 실패한 경우 (예: 권한 부족)