Files
TripLog/src/Controllers/AttachmentController.php
T

114 lines
3.9 KiB
PHP
Raw Normal View History

<?php
declare(strict_types=1);
namespace App\Controllers;
use App\Access;
use App\Attachments;
use App\Auth;
use App\Settings;
use App\Storage;
use App\Uploads;
class AttachmentController
{
public function node(): void
{
$this->requireLogin();
$nodeId = (int)($_GET['id'] ?? 0);
$node = Access::node($nodeId); // 含 family_id / activity_id / activity_title
view('nodes/attachments', [
'node' => $node,
'attachments' => Attachments::forTarget(Attachments::TARGET_NODE, $nodeId),
'maxMb' => (int)Settings::get('photo_max_mb', '12'),
'driverLabel' => Storage::label(),
'pageTitle' => '节点照片:' . ($node['title'] ?: '无标题'),
]);
}
/**
* 上传附件。
*
* 一个请求里同时收两类东西:
* files[] —— 普通 multipart 文件(照片原图、PDF、视频……)
* cropped[] —— 前端裁好的图片,JSON 字符串 {"name":..,"data":"data:image/...;base64,.."}
* 分开传是为了省流量:没裁的照片原图直传,裁过的才走 base64。
*
* 真正的处理逻辑在 App\Uploads —— 节点表单里的附件走同一份代码,
* 免得两边的 MIME 白名单/大小上限/孤儿文件回滚各写一套、慢慢走偏。
*/
public function upload(): void
{
$this->requireLogin();
// 请求体超过 post_max_size 时 PHP 会把 $_POST 和 $_FILES 整个丢掉,
// 紧接着的 csrf_check() 必然失败、报「请求已过期」,用户完全看不懂。
// 提前认出来,给一句能行动的话。
if (Uploads::requestTooLarge()) {
$nodeId = (int)($_POST['node_id'] ?? $_GET['node_id'] ?? 0);
flash('error', '一次上传的内容超过服务器上限(' . ini_get('post_max_size')
. '),请分批上传。');
redirect('attachments/node', ['id' => $nodeId]);
}
csrf_check();
$nodeId = (int)($_POST['node_id'] ?? 0);
$node = Access::node($nodeId);
$uploaderId = (int)Auth::id();
$target = Attachments::TARGET_NODE;
$files = Uploads::handleFiles($_FILES['files'] ?? [], $target, $nodeId, $uploaderId, 0);
$crop = Uploads::handleCropped((string)($_POST['cropped'] ?? ''), $target, $nodeId,
$uploaderId, $files['count']);
$uploaded = $files['uploaded'] + $crop['uploaded'];
$errors = array_merge($files['errors'], $crop['errors']);
$count = $crop['count'];
if ($count === 0 && $uploaded === 0) {
flash('error', '没有选择任何文件');
redirect('attachments/node', ['id' => $nodeId]);
}
$msg = "成功上传 {$uploaded} 个文件(存到" . Storage::label() . ')';
if (!empty($errors)) {
$msg .= ';失败 ' . count($errors) . ' 个:' . implode(';', array_slice($errors, 0, 5));
}
if (Storage::ossMisconfigured()) {
$msg .= '|注意:已选择 OSS 但配置不完整,本次实际存到了本站服务器';
}
flash('error', $msg);
redirect('attachments/node', ['id' => $nodeId], 'photos');
}
public function destroy(): void
{
$this->requireLogin();
csrf_check();
$id = (int)($_POST['id'] ?? 0);
$att = Attachments::find($id);
if (!$att || $att['target_type'] !== Attachments::TARGET_NODE) {
flash('error', '附件不存在');
redirect('activities/index');
}
$node = Access::node((int)$att['target_id']); // 顺带做归属校验
Attachments::delete($id);
flash('error', '照片已删除');
redirect('attachments/node', ['id' => (int)$node['id']], 'photos');
}
private function requireLogin(): void
{
if (!Auth::check()) {
redirect('auth/login');
}
}
}