storeFileAndMetadata moves the temporary file to its final path based on metadata and stores the metadata in the database See getPathFromMediaMetadata in fileutils for details of the final path. The order of operations is important as it avoids metadata entering the database before the file is ready
( ctx context.Context, tmpDir types.Path, absBasePath config.Path, db storage.Database, thumbnailSizes []config.ThumbnailSize, activeThumbnailGeneration *types.ActiveThumbnailGeneration, maxThumbnailGenerators int, )
| 277 | // is ready, and if we fail to move the file, it never gets added to the database. |
| 278 | // Returns a util.JSONResponse error and cleans up directories in case of error. |
| 279 | func (r *uploadRequest) storeFileAndMetadata( |
| 280 | ctx context.Context, |
| 281 | tmpDir types.Path, |
| 282 | absBasePath config.Path, |
| 283 | db storage.Database, |
| 284 | thumbnailSizes []config.ThumbnailSize, |
| 285 | activeThumbnailGeneration *types.ActiveThumbnailGeneration, |
| 286 | maxThumbnailGenerators int, |
| 287 | ) *util.JSONResponse { |
| 288 | finalPath, duplicate, err := fileutils.MoveFileWithHashCheck(tmpDir, r.MediaMetadata, absBasePath, r.Logger) |
| 289 | if err != nil { |
| 290 | r.Logger.WithError(err).Error("Failed to move file.") |
| 291 | return &util.JSONResponse{ |
| 292 | Code: http.StatusBadRequest, |
| 293 | JSON: jsonerror.Unknown("Failed to upload"), |
| 294 | } |
| 295 | } |
| 296 | if duplicate { |
| 297 | r.Logger.WithField("dst", finalPath).Info("File was stored previously - discarding duplicate") |
| 298 | } |
| 299 | |
| 300 | if err = db.StoreMediaMetadata(ctx, r.MediaMetadata); err != nil { |
| 301 | r.Logger.WithError(err).Warn("Failed to store metadata") |
| 302 | // If the file is a duplicate (has the same hash as an existing file) then |
| 303 | // there is valid metadata in the database for that file. As such we only |
| 304 | // remove the file if it is not a duplicate. |
| 305 | if !duplicate { |
| 306 | fileutils.RemoveDir(types.Path(path.Dir(string(finalPath))), r.Logger) |
| 307 | } |
| 308 | return &util.JSONResponse{ |
| 309 | Code: http.StatusBadRequest, |
| 310 | JSON: jsonerror.Unknown("Failed to upload"), |
| 311 | } |
| 312 | } |
| 313 | |
| 314 | go func() { |
| 315 | file, err := os.Open(string(finalPath)) |
| 316 | if err != nil { |
| 317 | r.Logger.WithError(err).Error("unable to open file") |
| 318 | return |
| 319 | } |
| 320 | defer file.Close() // nolint: errcheck |
| 321 | // http.DetectContentType only needs 512 bytes |
| 322 | buf := make([]byte, 512) |
| 323 | _, err = file.Read(buf) |
| 324 | if err != nil { |
| 325 | r.Logger.WithError(err).Error("unable to read file") |
| 326 | return |
| 327 | } |
| 328 | // Check if we need to generate thumbnails |
| 329 | fileType := http.DetectContentType(buf) |
| 330 | if !strings.HasPrefix(fileType, "image") { |
| 331 | r.Logger.WithField("contentType", fileType).Debugf("uploaded file is not an image or can not be thumbnailed, not generating thumbnails") |
| 332 | return |
| 333 | } |
| 334 | |
| 335 | busy, err := thumbnailer.GenerateThumbnails( |
| 336 | context.Background(), finalPath, thumbnailSizes, r.MediaMetadata, |