11# coding=utf-8
2+ import re
23import urllib
34
45import uuid_utils .compat as uuid
5455 "csv" : "text/csv" , "tsv" : "text/tab-separated-values" , "ics" : "text/calendar" ,
5556}
5657
58+ # 如果是音频文件并且有range请求,处理部分内容
59+ audio_types = ['mp3' , 'wav' , 'ogg' , 'flac' , 'aac' , 'opus' , 'm4a' ]
60+
5761
5862class FileSerializer (serializers .Serializer ):
5963 file = UploadedFileField (required = True , label = _ ('file' ))
@@ -85,6 +89,10 @@ def upload(self, with_valid=True):
8589
8690 class Operate (serializers .Serializer ):
8791 id = serializers .UUIDField (required = True )
92+ http_range = serializers .CharField (
93+ required = False , allow_blank = True , allow_null = True , label = _ ('HTTP Range' ),
94+ help_text = _ ('HTTP Range header for partial content requests, e.g., "bytes=0-1023"' )
95+ )
8896
8997 def get (self , with_valid = True ):
9098 if with_valid :
@@ -96,16 +104,53 @@ def get(self, with_valid=True):
96104 file_type = file .file_name .split ("." )[- 1 ].lower ()
97105 content_type = mime_types .get (file_type , 'application/octet-stream' )
98106 encoded_filename = urllib .parse .quote (file .file_name )
107+ # 获取文件内容
108+ file_bytes = file .get_bytes ()
109+ file_size = len (file_bytes )
110+
111+ response = None
112+ if file_type in audio_types and self .data .get ('http_range' ):
113+ response = self .handle_audio (file_size , file_bytes , content_type , encoded_filename )
114+ if response :
115+ return response
116+
117+ # 对于非范围请求或其他类型文件,返回完整内容
99118 headers = {
100119 'Content-Type' : content_type ,
101120 'Content-Disposition' : f'{ "inline" if file_type == "pdf" else "attachment" } ; filename={ encoded_filename } '
102121 }
103122 return HttpResponse (
104- file . get_bytes () ,
123+ file_bytes ,
105124 status = 200 ,
106125 headers = headers
107126 )
108127
128+ def handle_audio (self , file_size , file_bytes , content_type , encoded_filename ):
129+
130+ # 解析range请求 (格式如 "bytes=0-1023")
131+ range_match = re .match (r'bytes=(\d+)-(\d*)' , self .data .get ('http_range' , '' ))
132+ if range_match :
133+ start = int (range_match .group (1 ))
134+ end = int (range_match .group (2 )) if range_match .group (2 ) else file_size - 1
135+
136+ # 确保范围合法
137+ end = min (end , file_size - 1 )
138+ length = end - start + 1
139+
140+ # 创建部分响应
141+ response = HttpResponse (
142+ file_bytes [start :start + length ],
143+ status = 206 ,
144+ content_type = content_type
145+ )
146+
147+ # 设置部分内容响应头
148+ response ['Content-Range' ] = f'bytes { start } -{ end } /{ file_size } '
149+ response ['Accept-Ranges' ] = 'bytes'
150+ response ['Content-Length' ] = str (length )
151+ response ['Content-Disposition' ] = f'inline; filename={ encoded_filename } '
152+ return response
153+
109154 def delete (self ):
110155 self .is_valid (raise_exception = True )
111156 file_id = self .data .get ('id' )
0 commit comments