package documentdownload import ( "context" "fmt" "io" "mime" "path/filepath" "github.com/aws/aws-sdk-go-v2/service/s3" "github.com/google/uuid" ) // DownloadResult contains the data needed to stream a document file to the client. type DownloadResult struct { Body io.ReadCloser // S3 object body - caller must close Filename string // Original filename (for Content-Disposition) ContentType string // MIME type detected from filename extension Size int64 // File size in bytes (for Content-Length) } // GetDownload retrieves the document file bytes from S3. // Returns a DownloadResult with a streaming body that the caller must close. func (s *Service) GetDownload(ctx context.Context, id uuid.UUID) (*DownloadResult, error) { // Get document metadata (filename) from DB doc, err := s.cfg.GetDBQueries().GetDocumentEnriched(ctx, id) if err != nil { return nil, fmt.Errorf("document not found: %w", err) } // Get S3 entry (bucket, key) from documentEntries entry, err := s.cfg.GetDBQueries().GetDocumentEntry(ctx, id) if err != nil { return nil, fmt.Errorf("no S3 entry for document: %w", err) } // GetObject from S3 output, err := s.cfg.GetStoreClient().GetObject(ctx, &s3.GetObjectInput{ Bucket: &entry.Bucket, Key: &entry.Key, }) if err != nil { return nil, fmt.Errorf("failed to retrieve file from S3: %w", err) } // Detect MIME type from filename extension filename := "" if doc.Filename != nil { filename = *doc.Filename } contentType := detectContentType(filename) // Get file size var size int64 if output.ContentLength != nil { size = *output.ContentLength } return &DownloadResult{ Body: output.Body, Filename: filename, ContentType: contentType, Size: size, }, nil } // detectContentType determines the MIME type based on file extension. func detectContentType(filename string) string { if filename == "" { return "application/octet-stream" } contentType := mime.TypeByExtension(filepath.Ext(filename)) if contentType == "" { return "application/octet-stream" } return contentType }