|
|
<?php
|
|
|
|
|
|
namespace App\Models;
|
|
|
|
|
|
use Illuminate\Database\Eloquent\Model;
|
|
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
use Illuminate\Support\Facades\URL;
|
|
|
|
|
|
class ApplicationFile extends Model
|
|
|
{
|
|
|
protected $fillable = [
|
|
|
'application_id',
|
|
|
'kind',
|
|
|
'disk',
|
|
|
'path',
|
|
|
'original_name',
|
|
|
'size',
|
|
|
'mime',
|
|
|
];
|
|
|
|
|
|
public function application(): BelongsTo
|
|
|
{
|
|
|
return $this->belongsTo(Application::class);
|
|
|
}
|
|
|
|
|
|
public function publicUrl(): string
|
|
|
{
|
|
|
$u = Storage::disk($this->disk)->url($this->path);
|
|
|
|
|
|
return preg_replace('#([^:])//+#', '$1/', $u) ?? $u;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 浏览器下载/「另存为」显示名:优先用户上传时的原始文件名,必要时按存储路径补全扩展名(避免无后缀时落到随机 storage 名)。
|
|
|
*/
|
|
|
public function clientDownloadName(): string
|
|
|
{
|
|
|
$raw = $this->original_name !== null && trim($this->original_name) !== ''
|
|
|
? basename(str_replace(['\\', '/'], '_', $this->original_name))
|
|
|
: '';
|
|
|
$storageExt = strtolower((string) pathinfo($this->path, PATHINFO_EXTENSION));
|
|
|
|
|
|
if ($raw !== '') {
|
|
|
$baseExt = strtolower((string) pathinfo($raw, PATHINFO_EXTENSION));
|
|
|
if ($storageExt !== '' && $baseExt === '') {
|
|
|
$raw .= '.'.$storageExt;
|
|
|
}
|
|
|
|
|
|
return $raw;
|
|
|
}
|
|
|
|
|
|
return $storageExt !== '' ? '附件.'.$storageExt : '附件';
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* PDF/图片在浏览器内联预览,其余类型用 attachment 以触发「保存为」时使用 clientDownloadName,避免地址栏/标题仍显示 storage 随机名。
|
|
|
*/
|
|
|
public function preferredStreamDisposition(): string
|
|
|
{
|
|
|
$mime = strtolower((string) ($this->mime ?? ''));
|
|
|
if ($mime !== '') {
|
|
|
if (str_starts_with($mime, 'image/') || $mime === 'application/pdf') {
|
|
|
return 'inline';
|
|
|
}
|
|
|
|
|
|
return 'attachment';
|
|
|
}
|
|
|
|
|
|
$ext = strtolower((string) pathinfo($this->path, PATHINFO_EXTENSION));
|
|
|
if ($ext === 'pdf') {
|
|
|
return 'inline';
|
|
|
}
|
|
|
|
|
|
return 'attachment';
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 选手端「预览」用 URL:走 web 签名路由直出文件,不依赖 public/storage 符号链接(生产环境未 link 时 /storage/... 会 404)。
|
|
|
*/
|
|
|
public function participantPreviewSignedUrl(int $ttlSeconds = 172800): string
|
|
|
{
|
|
|
$u = URL::temporarySignedRoute(
|
|
|
'participant.application-file.download',
|
|
|
now()->addSeconds(max(60, $ttlSeconds)),
|
|
|
['file' => $this->id],
|
|
|
);
|
|
|
|
|
|
return preg_replace('#([^:])//+#', '$1/', $u) ?? $u;
|
|
|
}
|
|
|
}
|