Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/django/core/files/uploadhandler.py: 43%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

82 statements  

1""" 

2Base file upload handler classes, and the built-in concrete subclasses 

3""" 

4 

5import os 

6from io import BytesIO 

7 

8from django.conf import settings 

9from django.core.files.uploadedfile import InMemoryUploadedFile, TemporaryUploadedFile 

10from django.utils.module_loading import import_string 

11 

12__all__ = [ 

13 "UploadFileException", 

14 "StopUpload", 

15 "SkipFile", 

16 "FileUploadHandler", 

17 "TemporaryFileUploadHandler", 

18 "MemoryFileUploadHandler", 

19 "load_handler", 

20 "StopFutureHandlers", 

21] 

22 

23 

24class UploadFileException(Exception): 

25 """ 

26 Any error having to do with uploading files. 

27 """ 

28 

29 pass 

30 

31 

32class StopUpload(UploadFileException): 

33 """ 

34 This exception is raised when an upload must abort. 

35 """ 

36 

37 def __init__(self, connection_reset=False): 

38 """ 

39 If ``connection_reset`` is ``True``, Django knows will halt the upload 

40 without consuming the rest of the upload. This will cause the browser to 

41 show a "connection reset" error. 

42 """ 

43 self.connection_reset = connection_reset 

44 

45 def __str__(self): 

46 if self.connection_reset: 

47 return "StopUpload: Halt current upload." 

48 else: 

49 return "StopUpload: Consume request data, then halt." 

50 

51 

52class SkipFile(UploadFileException): 

53 """ 

54 This exception is raised by an upload handler that wants to skip a given file. 

55 """ 

56 

57 pass 

58 

59 

60class StopFutureHandlers(UploadFileException): 

61 """ 

62 Upload handlers that have handled a file and do not want future handlers to 

63 run should raise this exception instead of returning None. 

64 """ 

65 

66 pass 

67 

68 

69class FileUploadHandler: 

70 """ 

71 Base class for streaming upload handlers. 

72 """ 

73 

74 chunk_size = 64 * 2**10 # : The default chunk size is 64 KB. 

75 

76 def __init__(self, request=None): 

77 self.file_name = None 

78 self.content_type = None 

79 self.content_length = None 

80 self.charset = None 

81 self.content_type_extra = None 

82 self.request = request 

83 

84 def handle_raw_input( 

85 self, input_data, META, content_length, boundary, encoding=None 

86 ): 

87 """ 

88 Handle the raw input from the client. 

89 

90 Parameters: 

91 

92 :input_data: 

93 An object that supports reading via .read(). 

94 :META: 

95 ``request.META``. 

96 :content_length: 

97 The (integer) value of the Content-Length header from the 

98 client. 

99 :boundary: The boundary from the Content-Type header. Be sure to 

100 prepend two '--'. 

101 """ 

102 pass 

103 

104 def new_file( 

105 self, 

106 field_name, 

107 file_name, 

108 content_type, 

109 content_length, 

110 charset=None, 

111 content_type_extra=None, 

112 ): 

113 """ 

114 Signal that a new file has been started. 

115 

116 Warning: As with any data from the client, you should not trust 

117 content_length (and sometimes won't even get it). 

118 """ 

119 self.field_name = field_name 

120 self.file_name = file_name 

121 self.content_type = content_type 

122 self.content_length = content_length 

123 self.charset = charset 

124 self.content_type_extra = content_type_extra 

125 

126 def receive_data_chunk(self, raw_data, start): 

127 """ 

128 Receive data from the streamed upload parser. ``start`` is the position 

129 in the file of the chunk. 

130 """ 

131 raise NotImplementedError( 

132 "subclasses of FileUploadHandler must provide a receive_data_chunk() method" 

133 ) 

134 

135 def file_complete(self, file_size): 

136 """ 

137 Signal that a file has completed. File size corresponds to the actual 

138 size accumulated by all the chunks. 

139 

140 Subclasses should return a valid ``UploadedFile`` object. 

141 """ 

142 raise NotImplementedError( 

143 "subclasses of FileUploadHandler must provide a file_complete() method" 

144 ) 

145 

146 def upload_complete(self): 

147 """ 

148 Signal that the upload is complete. Subclasses should perform cleanup 

149 that is necessary for this handler. 

150 """ 

151 pass 

152 

153 def upload_interrupted(self): 

154 """ 

155 Signal that the upload was interrupted. Subclasses should perform 

156 cleanup that is necessary for this handler. 

157 """ 

158 pass 

159 

160 

161class TemporaryFileUploadHandler(FileUploadHandler): 

162 """ 

163 Upload handler that streams data into a temporary file. 

164 """ 

165 

166 def new_file(self, *args, **kwargs): 

167 """ 

168 Create the file object to append to as data is coming in. 

169 """ 

170 super().new_file(*args, **kwargs) 

171 self.file = TemporaryUploadedFile( 

172 self.file_name, self.content_type, 0, self.charset, self.content_type_extra 

173 ) 

174 

175 def receive_data_chunk(self, raw_data, start): 

176 self.file.write(raw_data) 

177 

178 def file_complete(self, file_size): 

179 self.file.seek(0) 

180 self.file.size = file_size 

181 return self.file 

182 

183 def upload_interrupted(self): 

184 if hasattr(self, "file"): 

185 temp_location = self.file.temporary_file_path() 

186 try: 

187 self.file.close() 

188 os.remove(temp_location) 

189 except FileNotFoundError: 

190 pass 

191 

192 

193class MemoryFileUploadHandler(FileUploadHandler): 

194 """ 

195 File upload handler to stream uploads into memory (used for small files). 

196 """ 

197 

198 def handle_raw_input( 

199 self, input_data, META, content_length, boundary, encoding=None 

200 ): 

201 """ 

202 Use the content_length to signal whether or not this handler should be 

203 used. 

204 """ 

205 # Check the content-length header to see if we should 

206 # If the post is too large, we cannot use the Memory handler. 

207 self.activated = content_length <= settings.FILE_UPLOAD_MAX_MEMORY_SIZE 

208 

209 def new_file(self, *args, **kwargs): 

210 super().new_file(*args, **kwargs) 

211 if self.activated: 

212 self.file = BytesIO() 

213 raise StopFutureHandlers() 

214 

215 def receive_data_chunk(self, raw_data, start): 

216 """Add the data to the BytesIO file.""" 

217 if self.activated: 

218 self.file.write(raw_data) 

219 else: 

220 return raw_data 

221 

222 def file_complete(self, file_size): 

223 """Return a file object if this handler is activated.""" 

224 if not self.activated: 

225 return 

226 

227 self.file.seek(0) 

228 return InMemoryUploadedFile( 

229 file=self.file, 

230 field_name=self.field_name, 

231 name=self.file_name, 

232 content_type=self.content_type, 

233 size=file_size, 

234 charset=self.charset, 

235 content_type_extra=self.content_type_extra, 

236 ) 

237 

238 

239def load_handler(path, *args, **kwargs): 

240 """ 

241 Given a path to a handler, return an instance of that handler. 

242 

243 E.g.:: 

244 >>> from django.http import HttpRequest 

245 >>> request = HttpRequest() 

246 >>> load_handler( 

247 ... 'django.core.files.uploadhandler.TemporaryFileUploadHandler', 

248 ... request, 

249 ... ) 

250 <TemporaryFileUploadHandler object at 0x...> 

251 """ 

252 return import_string(path)(*args, **kwargs)