根据您的应用结构,我准备了一些代码,用于提交表单并上传多个文件。它应该让您了解工作流程。
请注意,您应该利用:
一些资源:
一些系列:
一)
- Building a Domain Model - An Introduction to Persistence Agnosticism
- Building a Domain Model – Integrating Data Mappers
- An Introduction to Services
- Handling Collections of Aggregate Roots – the Repository Pattern
B)
- An Introduction to the Front Controller Pattern, Part 1
- An Introduction to the Front Controller Pattern, Part 2
controllers/Test.php
可通过路线访问:http://[your-host]/test
<?php
class Test extends Controller {
private $uploader;
public function __construct() {
$this->uploader = new Uploader();
}
public function index($extra = []) {
$data = array_merge([], $extra);
$this->view('test/index', $data);
}
public function upload() {
$data = [];
// Get the test name.
$testName = $_POST['test_name'] ?? '';
// Validate the test name.
if (empty($testName)) {
$errors['test_name'] = 'Please enter student name';
}
// Check the files list.
try {
if (!$_FILES) {
throw new UnexpectedValueException(
'There was a problem with the upload. Please try again.'
);
}
} catch (Exception $exc) {
echo $exc->getMessage();
exit();
}
// If no errors, then upload the files.
if (empty($errors)) {
$uploadResult = $this->uploader->upload($_FILES['files']);
if ($uploadResult !== TRUE) {
$errors['files'] = $uploadResult;
}
}
$data['test_name'] = $testName;
$data['errors'] = $errors ?? [];
// Flash some success message using the flash() function if no errors occurred...
$this->index($data);
}
}
库/Uploader.php
您可以/应该将其拆分为更多方法。
<?php
class Uploader {
/**
* If the value is set to a relative path, then the given path is
* relative to the document root, e.g. to the "public" directory.
*/
const UPLOAD_DIR = APPROOT . '/uploads/' /* This is an absolute path */;
const UPLOAD_DIR_ACCESS_MODE = 0777;
const UPLOAD_MAX_FILE_SIZE = 10485760;
const UPLOAD_ALLOWED_MIME_TYPES = [
'image/jpeg',
'image/png',
'image/gif',
];
/**
*
*/
public function __construct() {
}
/**
* Upload the files list.
*
* @param array $files (optional) Files list - as received from $_FILES variable.
* @return bool|string[] TRUE on success, or a list of errors on failure.
*/
public function upload(array $files = []) {
// Normalize the files list.
$normalizedFiles = $this->normalizeFiles($files);
// Upload each file.
foreach ($normalizedFiles as $normalizedFile) {
$uploadResult = $this->uploadFile($normalizedFile);
if ($uploadResult !== TRUE) {
$errors[] = $uploadResult;
}
}
// Return TRUE on success, or the errors list on failure.
return empty($errors) ? TRUE : $errors;
}
/**
* Normalize the files list.
*
* @link https://www.php-fig.org/psr/psr-7/#16-uploaded-files PSR-7: 1.6 Uploaded files.
*
* @param array $files (optional) Files list - as received from $_FILES variable.
* @return array Normalized files list.
*/
private function normalizeFiles(array $files = []) {
$normalizedFiles = [];
foreach ($files as $filesKey => $filesItem) {
foreach ($filesItem as $itemKey => $itemValue) {
$normalizedFiles[$itemKey][$filesKey] = $itemValue;
}
}
return $normalizedFiles;
}
/**
* Upload a file.
*
* @param array $file A normalized file item - as received from $_FILES variable.
* @return bool|string TRUE on success, or an error string on failure.
*/
private function uploadFile(array $file = []) {
$name = $file['name'];
$type = $file['type'];
$tmpName = $file['tmp_name'];
$error = $file['error'];
$size = $file['size'];
/*
* Validate the file error. The actual upload takes place when the file
* error is UPLOAD_ERR_OK (the first case in this switch statement).
*
* @link https://secure.php.net/manual/en/features.file-upload.errors.php Error Messages Explained.
*/
switch ($error) {
case UPLOAD_ERR_OK: /* There is no error, the file can be uploaded. */
// Validate the file size.
if ($size > self::UPLOAD_MAX_FILE_SIZE) {
return sprintf('The size of the file "%s" exceeds the maximal allowed size (%s Byte).'
, $name
, self::UPLOAD_MAX_FILE_SIZE
);
}
// Validate the file type.
if (!in_array($type, self::UPLOAD_ALLOWED_MIME_TYPES)) {
return sprintf('The file "%s" is not of a valid MIME type. Allowed MIME types: %s.'
, $name
, implode(', ', self::UPLOAD_ALLOWED_MIME_TYPES)
);
}
// Define the upload path.
$uploadDirPath = rtrim(self::UPLOAD_DIR, '/');
$uploadPath = $uploadDirPath . '/' . $name;
// Create the upload directory.
$this->createDirectory($uploadDirPath);
// Move the file to the new location.
if (!move_uploaded_file($tmpName, $uploadPath)) {
return sprintf('The file "%s" could not be moved to the specified location.'
, $name
);
}
return TRUE;
break;
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
return sprintf('The provided file "%s" exceeds the allowed file size.'
, $name
);
break;
case UPLOAD_ERR_PARTIAL:
return sprintf('The provided file "%s" was only partially uploaded.'
, $name
);
break;
case UPLOAD_ERR_NO_FILE:
return 'No file provided. Please select at least one file.';
break;
//...
// AND SO ON FOR THE OTHER FILE ERROR TYPES...
//...
default:
return 'There was a problem with the upload. Please try again.';
break;
}
return TRUE;
}
/**
* Create a directory at the specified path.
*
* @param string $path Directory path.
* @return $this
*/
private function createDirectory(string $path) {
try {
if (file_exists($path) && !is_dir($path)) {
throw new UnexpectedValueException(
'The upload directory can not be created because '
. 'a file having the same name already exists!'
);
}
} catch (Exception $exc) {
echo $exc->getMessage();
exit();
}
if (!is_dir($path)) {
mkdir($path, self::UPLOAD_DIR_ACCESS_MODE, TRUE);
}
return $this;
}
}
views/test/index.php
注意表单操作:http://[your-host]/test/upload。它指向Test 控制器和upload 方法。好吧,我将方法命名为upload,以表明它是关于上传过程的测试。虽然我应该将其命名为 save 或类似名称,以反映一个事实,即要保存一些数据,包括上传的文件。
<?php require APPROOT . '/views/inc/header.php'; ?>
<div class="container">
<div class="row">
<div class="sm-12 col">
<h2>Test upload</h2>
<p>Submit the form</p>
<form action="<?php echo URLROOT; ?>/test/upload" method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="test_name">Test name <sup>*</sup></label>
<input type="text" id="test_name" name="test_name" class="form-control form-control-lg <?php echo empty($data['errors']['test_name']) ? '' : 'is-invalid'; ?>" value="<?php echo $data['test_name'] ?? ''; ?>" />
<span class="invalid-feedback"><?php echo $data['errors']['test_name'] ?? ''; ?></span>
</div>
<div class="form-group">
<label for="myFiles">Files <sup>*</sup></label></label>
<input type="file" id="myFiles" name="files[]" multiple class="form-control form-control-lg <?php echo empty($data['errors']['files']) ? '' : 'is-invalid'; ?>" />
<span class="invalid-feedback"><?php echo empty($data['errors']['files']) ? '' : implode('<br/>', $data['errors']['files']); ?></span>
</div>
<input type="submit" value="Submit" class="btn btn-success">
</form>
</div>
</div>
</div>
<?php require APPROOT . '/views/inc/footer.php'; ?>