protected function handlePost() : HttpResponse
    {
        $fileName   = $this->getRequest()->extractFileName();
        $uploadType = self::UPLOAD_TYPE_NORMAL;

        if (empty($fileName)) {
            return $this->response->send(null, HttpResponse::HTTP_BAD_REQUEST);
        }

        if ( ! $this->verifyUploadSize()) {
            return $this->response->send(null, HttpResponse::HTTP_REQUEST_ENTITY_TOO_LARGE);
        }

        $uploadKey = $this->getUploadKey();
        $filePath  = $this->uploadDir . DIRECTORY_SEPARATOR . $fileName;

        if ($this->getRequest()->isFinal()) {
            return $this->handleConcatenation($fileName, $filePath);
        }

        if ($this->getRequest()->isPartial()) {
            $filePath   = $this->getPathForPartialUpload($uploadKey) . $fileName;
            $uploadType = self::UPLOAD_TYPE_PARTIAL;
        }

        $checksum = $this->getClientChecksum();
        $location = $this->getRequest()->url() . $this->getApiPath() . '/' . $uploadKey;

        $file = $this->buildFile([
            'name' => $fileName,
            'offset' => 0,
            'size' => $this->getRequest()->header('Upload-Length'),
            'file_path' => $filePath,
            'location' => $location,
        ])->setKey($uploadKey)->setChecksum($checksum);

        $this->cache->set($uploadKey, $file->details() + ['upload_type' => $uploadType]);

        $this->event()->dispatch(
            UploadCreated::NAME,
            new UploadCreated($file, $this->getRequest(), $this->getResponse())
        );

        return $this->response->send(
            null,
            HttpResponse::HTTP_CREATED,
            [
                'Location' => $location,
                'Upload-Expires' => $this->cache->get($uploadKey)['expires_at'],
            ]
        );
    }