Skip to content

Commit c1e1a19

Browse files
authored
feat: 添加文件上传接口 (#712)
1 parent d152f44 commit c1e1a19

File tree

7 files changed

+188
-1
lines changed

7 files changed

+188
-1
lines changed

apps/common/field/common.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,11 @@ def __init__(self, **kwargs):
4040

4141
def to_representation(self, value):
4242
return value
43+
44+
45+
class UploadedFileField(serializers.FileField):
46+
def __init__(self, **kwargs):
47+
super().__init__(**kwargs)
48+
49+
def to_representation(self, value):
50+
return value

apps/dataset/migrations/0005_file.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Generated by Django 4.2.13 on 2024-07-05 18:59
2+
3+
from django.db import migrations, models
4+
import uuid
5+
6+
from smartdoc.const import CONFIG
7+
8+
9+
class Migration(migrations.Migration):
10+
dependencies = [
11+
('dataset', '0004_document_directly_return_similarity'),
12+
]
13+
14+
operations = [
15+
migrations.RunSQL(f"grant execute on function lo_from_bytea to {CONFIG.get('DB_USER')}"),
16+
migrations.CreateModel(
17+
name='File',
18+
fields=[
19+
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
20+
('update_time', models.DateTimeField(auto_now=True, verbose_name='修改时间')),
21+
('id', models.UUIDField(default=uuid.uuid1, editable=False, primary_key=True, serialize=False,
22+
verbose_name='主键id')),
23+
('file_name', models.CharField(default='', max_length=256, verbose_name='文件名称')),
24+
('loid', models.IntegerField(verbose_name='loid')),
25+
],
26+
options={
27+
'db_table': 'file',
28+
},
29+
),
30+
]

apps/dataset/models/data_set.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
from django.db import models
1212

13+
from common.db.sql_execute import select_one
1314
from common.mixins.app_model_mixin import AppModelMixin
1415
from users.models import User
1516

@@ -123,3 +124,26 @@ class Image(AppModelMixin):
123124

124125
class Meta:
125126
db_table = "image"
127+
128+
129+
class File(AppModelMixin):
130+
id = models.UUIDField(primary_key=True, max_length=128, default=uuid.uuid1, editable=False, verbose_name="主键id")
131+
132+
file_name = models.CharField(max_length=256, verbose_name="文件名称", default="")
133+
134+
loid = models.IntegerField(verbose_name="loid")
135+
136+
class Meta:
137+
db_table = "file"
138+
139+
def save(
140+
self, bytea=None, force_insert=False, force_update=False, using=None, update_fields=None
141+
):
142+
result = select_one("SELECT lo_from_bytea(%s, %s::bytea) as loid", [0, bytea])
143+
self.loid = result['loid']
144+
self.file_name = 'speech.mp3'
145+
super().save()
146+
147+
def get_byte(self):
148+
result = select_one(f'SELECT lo_get({self.loid}) as "data"', [])
149+
return result['data']
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# coding=utf-8
2+
"""
3+
@project: maxkb
4+
@Author:虎
5+
@file: image_serializers.py
6+
@date:2024/4/22 16:36
7+
@desc:
8+
"""
9+
import uuid
10+
11+
from django.db.models import QuerySet
12+
from django.http import HttpResponse
13+
from rest_framework import serializers
14+
15+
from common.exception.app_exception import NotFound404
16+
from common.field.common import UploadedFileField
17+
from common.util.field_message import ErrMessage
18+
from dataset.models import File
19+
20+
mime_types = {"html": "text/html", "htm": "text/html", "shtml": "text/html", "css": "text/css", "xml": "text/xml",
21+
"gif": "image/gif", "jpeg": "image/jpeg", "jpg": "image/jpeg", "js": "application/javascript",
22+
"atom": "application/atom+xml", "rss": "application/rss+xml", "mml": "text/mathml", "txt": "text/plain",
23+
"jad": "text/vnd.sun.j2me.app-descriptor", "wml": "text/vnd.wap.wml", "htc": "text/x-component",
24+
"avif": "image/avif", "png": "image/png", "svg": "image/svg+xml", "svgz": "image/svg+xml",
25+
"tif": "image/tiff", "tiff": "image/tiff", "wbmp": "image/vnd.wap.wbmp", "webp": "image/webp",
26+
"ico": "image/x-icon", "jng": "image/x-jng", "bmp": "image/x-ms-bmp", "woff": "font/woff",
27+
"woff2": "font/woff2", "jar": "application/java-archive", "war": "application/java-archive",
28+
"ear": "application/java-archive", "json": "application/json", "hqx": "application/mac-binhex40",
29+
"doc": "application/msword", "pdf": "application/pdf", "ps": "application/postscript",
30+
"eps": "application/postscript", "ai": "application/postscript", "rtf": "application/rtf",
31+
"m3u8": "application/vnd.apple.mpegurl", "kml": "application/vnd.google-earth.kml+xml",
32+
"kmz": "application/vnd.google-earth.kmz", "xls": "application/vnd.ms-excel",
33+
"eot": "application/vnd.ms-fontobject", "ppt": "application/vnd.ms-powerpoint",
34+
"odg": "application/vnd.oasis.opendocument.graphics",
35+
"odp": "application/vnd.oasis.opendocument.presentation",
36+
"ods": "application/vnd.oasis.opendocument.spreadsheet", "odt": "application/vnd.oasis.opendocument.text",
37+
"wmlc": "application/vnd.wap.wmlc", "wasm": "application/wasm", "7z": "application/x-7z-compressed",
38+
"cco": "application/x-cocoa", "jardiff": "application/x-java-archive-diff",
39+
"jnlp": "application/x-java-jnlp-file", "run": "application/x-makeself", "pl": "application/x-perl",
40+
"pm": "application/x-perl", "prc": "application/x-pilot", "pdb": "application/x-pilot",
41+
"rar": "application/x-rar-compressed", "rpm": "application/x-redhat-package-manager",
42+
"sea": "application/x-sea", "swf": "application/x-shockwave-flash", "sit": "application/x-stuffit",
43+
"tcl": "application/x-tcl", "tk": "application/x-tcl", "der": "application/x-x509-ca-cert",
44+
"pem": "application/x-x509-ca-cert", "crt": "application/x-x509-ca-cert",
45+
"xpi": "application/x-xpinstall", "xhtml": "application/xhtml+xml", "xspf": "application/xspf+xml",
46+
"zip": "application/zip", "bin": "application/octet-stream", "exe": "application/octet-stream",
47+
"dll": "application/octet-stream", "deb": "application/octet-stream", "dmg": "application/octet-stream",
48+
"iso": "application/octet-stream", "img": "application/octet-stream", "msi": "application/octet-stream",
49+
"msp": "application/octet-stream", "msm": "application/octet-stream", "mid": "audio/midi",
50+
"midi": "audio/midi", "kar": "audio/midi", "mp3": "audio/mpeg", "ogg": "audio/ogg", "m4a": "audio/x-m4a",
51+
"ra": "audio/x-realaudio", "3gpp": "video/3gpp", "3gp": "video/3gpp", "ts": "video/mp2t",
52+
"mp4": "video/mp4", "mpeg": "video/mpeg", "mpg": "video/mpeg", "mov": "video/quicktime",
53+
"webm": "video/webm", "flv": "video/x-flv", "m4v": "video/x-m4v", "mng": "video/x-mng",
54+
"asx": "video/x-ms-asf", "asf": "video/x-ms-asf", "wmv": "video/x-ms-wmv", "avi": "video/x-msvideo"}
55+
56+
57+
class FileSerializer(serializers.Serializer):
58+
file = UploadedFileField(required=True, error_messages=ErrMessage.image("文件"))
59+
60+
def upload(self, with_valid=True):
61+
if with_valid:
62+
self.is_valid(raise_exception=True)
63+
file_id = uuid.uuid1()
64+
file = File(id=file_id, file_name=self.data.get('file').name)
65+
file.save(self.data.get('file').read())
66+
return f'/api/file/{file_id}'
67+
68+
class Operate(serializers.Serializer):
69+
id = serializers.UUIDField(required=True)
70+
71+
def get(self, with_valid=True):
72+
if with_valid:
73+
self.is_valid(raise_exception=True)
74+
file_id = self.data.get('id')
75+
file = QuerySet(File).filter(id=file_id).first()
76+
if file is None:
77+
raise NotFound404(404, "不存在的文件")
78+
return HttpResponse(file.get_byte(), status=200,
79+
headers={'Content-Type': mime_types.get(file.file_name.split(".")[-1], 'text/plain')})

apps/dataset/urls.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,7 @@
5555
path('dataset/<str:dataset_id>/problem/<str:problem_id>', views.Problem.Operate.as_view()),
5656
path('dataset/<str:dataset_id>/problem/<str:problem_id>/paragraph', views.Problem.Paragraph.as_view()),
5757
path('image/<str:image_id>', views.Image.Operate.as_view()),
58-
path('image', views.Image.as_view())
58+
path('image', views.Image.as_view()),
59+
path('file/<str:file_id>', views.FileView.Operate.as_view()),
60+
path('file', views.FileView.as_view())
5961
]

apps/dataset/views/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@
1111
from .paragraph import *
1212
from .problem import *
1313
from .image import *
14+
from .file import *

apps/dataset/views/file.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# coding=utf-8
2+
"""
3+
@project: maxkb
4+
@Author:虎
5+
@file: image.py
6+
@date:2024/4/22 16:23
7+
@desc:
8+
"""
9+
from drf_yasg import openapi
10+
from drf_yasg.utils import swagger_auto_schema
11+
from rest_framework.decorators import action
12+
from rest_framework.parsers import MultiPartParser
13+
from rest_framework.views import APIView
14+
from rest_framework.views import Request
15+
16+
from common.auth import TokenAuth
17+
from common.response import result
18+
from dataset.serializers.file_serializers import FileSerializer
19+
20+
21+
class FileView(APIView):
22+
authentication_classes = [TokenAuth]
23+
parser_classes = [MultiPartParser]
24+
25+
@action(methods=['POST'], detail=False)
26+
@swagger_auto_schema(operation_summary="上传文件",
27+
operation_id="上传文件",
28+
manual_parameters=[openapi.Parameter(name='file',
29+
in_=openapi.IN_FORM,
30+
type=openapi.TYPE_FILE,
31+
required=True,
32+
description='上传文件')],
33+
tags=["文件"])
34+
def post(self, request: Request):
35+
return result.success(FileSerializer(data={'file': request.FILES.get('file')}).upload())
36+
37+
class Operate(APIView):
38+
@action(methods=['GET'], detail=False)
39+
@swagger_auto_schema(operation_summary="获取图片",
40+
operation_id="获取图片",
41+
tags=["文件"])
42+
def get(self, request: Request, file_id: str):
43+
return FileSerializer.Operate(data={'id': file_id}).get()

0 commit comments

Comments
 (0)