feat(note): add image upload functionality for notes
- Implemented a new endpoint in the Note controller to handle image uploads associated with notes. - Added the `uploadImage` method in NoteService to manage image storage and return public URLs. - Updated API routes to include the new image upload endpoint, enhancing note management capabilities.
This commit is contained in:
@@ -178,4 +178,27 @@ class Note extends BaseController
|
|||||||
return Response::error($e->getMessage(), $e->getCode() ?: 500);
|
return Response::error($e->getMessage(), $e->getCode() ?: 500);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传笔记图片
|
||||||
|
* POST /note/v1/item/image/:id
|
||||||
|
*/
|
||||||
|
public function image(Request $request, int $id)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if ($id <= 0) {
|
||||||
|
return Response::error('笔记 ID 不正确', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
$file = $request->file('image');
|
||||||
|
if (!$file) {
|
||||||
|
return Response::error('图片文件不能为空', 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $this->noteService->uploadImage($this->getCurrentNoteUserId(), $id, $file);
|
||||||
|
return Response::success($result, '上传成功');
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
return Response::error($e->getMessage(), $e->getCode() ?: 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ Route::group('v1', function () {
|
|||||||
Route::post('item/delete/:id', [Note::class, 'delete']);
|
Route::post('item/delete/:id', [Note::class, 'delete']);
|
||||||
Route::post('item/transcript/:id', [Note::class, 'transcript']);
|
Route::post('item/transcript/:id', [Note::class, 'transcript']);
|
||||||
Route::post('item/audio/:id', [Note::class, 'audio']);
|
Route::post('item/audio/:id', [Note::class, 'audio']);
|
||||||
|
Route::post('item/image/:id', [Note::class, 'image']);
|
||||||
|
|
||||||
Route::post('ai/summary/:id', [Ai::class, 'summary']);
|
Route::post('ai/summary/:id', [Ai::class, 'summary']);
|
||||||
Route::get('ai/summary/:id', [Ai::class, 'readSummary']);
|
Route::get('ai/summary/:id', [Ai::class, 'readSummary']);
|
||||||
|
|||||||
@@ -268,6 +268,33 @@ class NoteService
|
|||||||
return $this->formatAudio($audio);
|
return $this->formatAudio($audio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传笔记图片。
|
||||||
|
*
|
||||||
|
* 这里不额外建表,直接返回公开图片地址,由前端把图片标记写回 note.content,
|
||||||
|
* 以便在跨设备打开同一笔记时仍能恢复图片内容。
|
||||||
|
*
|
||||||
|
* @param int $noteUserId
|
||||||
|
* @param int $id
|
||||||
|
* @param File $file
|
||||||
|
* @return array
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
public function uploadImage(int $noteUserId, int $id, File $file): array
|
||||||
|
{
|
||||||
|
$this->getOwnedNote($noteUserId, $id);
|
||||||
|
$savedPath = str_replace('\\', '/', Filesystem::disk('public')->putFile('note/image', $file));
|
||||||
|
|
||||||
|
return [
|
||||||
|
'disk' => 'public',
|
||||||
|
'file_path' => $savedPath,
|
||||||
|
'image_url' => $this->buildPublicFileUrl($savedPath),
|
||||||
|
'file_size' => (int) $file->getSize(),
|
||||||
|
'mime_type' => $this->detectImageMimeType($savedPath, $file),
|
||||||
|
'updated_at' => time(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建分享
|
* 创建分享
|
||||||
*
|
*
|
||||||
@@ -444,6 +471,38 @@ class NoteService
|
|||||||
return $mimeMap[$extension] ?? 'application/octet-stream';
|
return $mimeMap[$extension] ?? 'application/octet-stream';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推断图片 MIME。
|
||||||
|
*
|
||||||
|
* 与音频上传一样,这里优先按扩展名兜底,避免依赖 fileinfo 扩展。
|
||||||
|
*
|
||||||
|
* @param string $savedPath
|
||||||
|
* @param File $file
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function detectImageMimeType(string $savedPath, File $file): string
|
||||||
|
{
|
||||||
|
$extension = strtolower((string) pathinfo($savedPath, PATHINFO_EXTENSION));
|
||||||
|
if ($extension === '') {
|
||||||
|
$extension = strtolower((string) pathinfo((string) $file->getOriginalName(), PATHINFO_EXTENSION));
|
||||||
|
}
|
||||||
|
|
||||||
|
$mimeMap = [
|
||||||
|
'avif' => 'image/avif',
|
||||||
|
'bmp' => 'image/bmp',
|
||||||
|
'gif' => 'image/gif',
|
||||||
|
'heic' => 'image/heic',
|
||||||
|
'heif' => 'image/heif',
|
||||||
|
'jpeg' => 'image/jpeg',
|
||||||
|
'jpg' => 'image/jpeg',
|
||||||
|
'png' => 'image/png',
|
||||||
|
'svg' => 'image/svg+xml',
|
||||||
|
'webp' => 'image/webp',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $mimeMap[$extension] ?? 'application/octet-stream';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 规范化标题
|
* 规范化标题
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ Route::group('note/v1', function () {
|
|||||||
Route::post('item/delete/:id', [NoteItem::class, 'delete']);
|
Route::post('item/delete/:id', [NoteItem::class, 'delete']);
|
||||||
Route::post('item/transcript/:id', [NoteItem::class, 'transcript']);
|
Route::post('item/transcript/:id', [NoteItem::class, 'transcript']);
|
||||||
Route::post('item/audio/:id', [NoteItem::class, 'audio']);
|
Route::post('item/audio/:id', [NoteItem::class, 'audio']);
|
||||||
|
Route::post('item/image/:id', [NoteItem::class, 'image']);
|
||||||
|
|
||||||
Route::post('ai/summary/:id', [NoteAi::class, 'summary']);
|
Route::post('ai/summary/:id', [NoteAi::class, 'summary']);
|
||||||
Route::get('ai/summary/:id', [NoteAi::class, 'readSummary']);
|
Route::get('ai/summary/:id', [NoteAi::class, 'readSummary']);
|
||||||
|
|||||||
Reference in New Issue
Block a user