使用POST表单上传文件

通过POST请求中使用表单上传的文件可以使用 Request 方法 getUploadedFiles() 来检索。

在使用POST请求上传文件时,确保文件上传表单具有属性 enctype="multipart/form-data" ,否则 getUploadedFiles() 将返回一个空数组。

如果为同一个输入名称上传了多个文件,请在HTML中的输入名称后面添加方括号,否则 getUploadedFiles() 只会为输入名称返回一个已上传文件。

下面是一个包含单个和多个文件上传的示例HTML表单。

<!-- 确保将属性enctype设置为multipart/form-data -->
<form method="post" enctype="multipart/form-data">
    <!-- 单文件上传 -->
    <p>
        <label>添加文件(单个): </label><br/>
        <input type="file" name="example1"/>
    </p>

    <!-- 同一个输入名称的多个输入字段,使用方括号 -->
    <p>
        <label>添加文件(最多2个): </label><br/>
        <input type="file" name="example2[]"/><br/>
        <input type="file" name="example2[]"/>
    </p>

    <!-- 允许上传多个文件的单个文件输入字段,使用方括号 -->
    <p>
        <label>添加文件(多个): </label><br/>
        <input type="file" name="example3[]" multiple="multiple"/>
    </p>

    <p>
        <input type="submit"/>
    </p>
</form>

可以使用 moveTo 方法将上传的文件移动到一个目录中。下面是一个处理上述HTML表单的已上传文件的示例应用程序。

<?php

use DI\ContainerBuilder;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\UploadedFileInterface;
use Slim\Factory\AppFactory;

require __DIR__ . '/../vendor/autoload.php';

$containerBuilder = new ContainerBuilder();
$container = $containerBuilder->build();

$container->set('upload_directory', __DIR__ . '/uploads');

AppFactory::setContainer($container);
$app = AppFactory::create();

$app->post('/', function (ServerRequestInterface $request, ResponseInterface $response) {
    $directory = $this->get('upload_directory');
    $uploadedFiles = $request->getUploadedFiles();

    // 处理单个输入的单个文件上传
    $uploadedFile = $uploadedFiles['example1'];
    if ($uploadedFile->getError() === UPLOAD_ERR_OK) {
        $filename = moveUploadedFile($directory, $uploadedFile);
        $response->getBody()->write('Uploaded: ' . $filename . '<br/>');
    }

    // 处理具有相同键的多个输入
    foreach ($uploadedFiles['example2'] as $uploadedFile) {
        if ($uploadedFile->getError() === UPLOAD_ERR_OK) {
            $filename = moveUploadedFile($directory, $uploadedFile);
            $response->getBody()->write('Uploaded: ' . $filename . '<br/>');
        }
    }

    // 处理单个输入的多个文件上传
    foreach ($uploadedFiles['example3'] as $uploadedFile) {
        if ($uploadedFile->getError() === UPLOAD_ERR_OK) {
            $filename = moveUploadedFile($directory, $uploadedFile);
            $response->getBody()->write('Uploaded: ' . $filename . '<br/>');
        }
    }

    return $response;
});

/**
 * 将上传的文件移动到上传目录,并为其分配一个唯一的名称,以避免覆盖现有的上传文件。
 *
 * @param string $directory 要将文件移动到的目录
 * @param UploadedFileInterface $uploadedFile 要移动的已上传文件
 *
 * @return string 移动后的文件名
 */
function moveUploadedFile(string $directory, UploadedFileInterface $uploadedFile)
{
    $extension = pathinfo($uploadedFile->getClientFilename(), PATHINFO_EXTENSION);

    // 参见http://php.net/manual/en/function.random-bytes.php
    $basename = bin2hex(random_bytes(8));
    $filename = sprintf('%s.%0.8s', $basename, $extension);

    $uploadedFile->moveTo($directory . DIRECTORY_SEPARATOR . $filename);

    return $filename;
}

$app->run();