avoid collisions and delete the thumnbnail after successful response

This commit is contained in:
2025-03-25 11:02:15 +01:00
parent 03ac9d3656
commit 269d7cfbf5
8 changed files with 46 additions and 61 deletions

View File

@@ -8,22 +8,22 @@ import (
"net"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
_ "image/gif"
"image/jpeg"
_ "image/jpeg"
"image/png"
_ "image/png"
pb "github.com/JuLi0n21/thumbnail_service/proto"
"github.com/google/uuid"
"github.com/nfnt/resize"
"google.golang.org/grpc"
)
// Helper function to generate video thumbnails using ffmpeg
func generateVideoThumbnail(inputPath, outputPath string, maxWidth, maxHeight int) error {
cmd := exec.Command("ffmpeg", "-i", inputPath, "-vf", "thumbnail", "-frames:v", "1", outputPath)
cmd := exec.Command("ffmpeg", "-y", "-i", inputPath, "-vf", "thumbnail", "-frames:v", "1", outputPath)
var stderr strings.Builder
cmd.Stderr = &stderr
@@ -39,24 +39,21 @@ func generateVideoThumbnail(inputPath, outputPath string, maxWidth, maxHeight in
return nil
}
// Helper function to generate PDF thumbnails using poppler-utils (pdftoppm)
func generatePdfThumbnail(inputPath, outputPath string, maxWidth, maxHeight int) error {
// Command for Poppler-utils to generate a thumbnail from the first page of a PDF file
cmd := exec.Command("pdftoppm", inputPath, outputPath, "-jpeg", "-f", "1", "-l", "1", "-scale-to", "200")
err := cmd.Run()
if err != nil {
return fmt.Errorf("failed to generate PDF thumbnail using Poppler-utils: %v", err)
}
outputFileWithPage := fmt.Sprintf("%s-01.jpg", outputPath) // pdftoppm output file with page number suffix
outputFileWithPage := fmt.Sprintf("%s-01.jpg", outputPath)
// Rename the file
err = os.Rename(outputFileWithPage, outputPath)
if err != nil {
return fmt.Errorf("failed to rename file: %v", err)
}
// Resize the generated image if maxWidth or maxHeight is provided
if maxWidth > 0 || maxHeight > 0 {
return resizeImage(outputPath, outputPath, maxWidth, maxHeight)
}
@@ -65,43 +62,39 @@ func generatePdfThumbnail(inputPath, outputPath string, maxWidth, maxHeight int)
}
func resizeImage(inputPath, outputPath string, maxWidth, maxHeight int) error {
// Open the input image file
file, err := os.Open(inputPath)
if err != nil {
return fmt.Errorf("failed to open image file: %v", err)
}
defer file.Close()
// Decode the image
img, imgType, err := image.Decode(file)
if err != nil {
return fmt.Errorf("failed to decode image: %v", err)
}
// Calculate the new dimensions, preserving the aspect ratio
var newWidth, newHeight int
if maxWidth > 0 && maxHeight > 0 {
// Resize with both width and height limit
newWidth = maxWidth
newHeight = maxHeight
} else if maxWidth > 0 {
// Resize based on width
newWidth = maxWidth
newHeight = int(float64(img.Bounds().Dy()) * float64(maxWidth) / float64(img.Bounds().Dx()))
} else if maxHeight > 0 {
// Resize based on height
newHeight = maxHeight
newWidth = int(float64(img.Bounds().Dx()) * float64(maxHeight) / float64(img.Bounds().Dy()))
} else {
// No resizing needed
newWidth = img.Bounds().Dx()
newHeight = img.Bounds().Dy()
}
// Resize the image using the calculated dimensions
resizedImg := resize.Resize(uint(newWidth), uint(newHeight), img, resize.Lanczos3)
// Create the output file
outFile, err := os.Create(outputPath)
if err != nil {
return fmt.Errorf("failed to create output file: %v", err)
@@ -130,79 +123,72 @@ type server struct {
}
func (s *server) GenerateThumbnail(ctx context.Context, req *pb.ThumbnailRequest) (*pb.ThumbnailResponse, error) {
// Create a temporary file to store the uploaded content
start := time.Now()
fmt.Println(start.Format("2006-01-02 15:04:05.000"), "Thumbnail request ", req.FileType, "H: ", req.MaxHeight, "W: ", req.MaxWidth)
tempFile, err := os.CreateTemp("", "upload-*")
if err != nil {
return nil, fmt.Errorf("failed to create temporary file: %v", err)
}
defer os.Remove(tempFile.Name()) // Cleanup temporary file
defer os.Remove(tempFile.Name())
// Write the content from the request to the temporary file
err = os.WriteFile(tempFile.Name(), req.FileContent, 0644)
if err != nil {
return nil, fmt.Errorf("failed to write content to file: %v", err)
}
// Generate thumbnail and save it to disk based on file type
var outputPath string
thumbnailName := fmt.Sprintf("thumbnail-%s.jpg", uuid.New().String())
outputPath := filepath.Join("thumbnails", thumbnailName)
if err := os.MkdirAll("thumbnails", 0755); err != nil {
return nil, fmt.Errorf("failed to create thumbnails directory: %v", err)
}
// Check file type using enum
switch req.FileType {
case pb.FileType_IMAGE:
// Image file, use ImageMagick or other Go logic to create a thumbnail
outputPath = "thumbnails/image-thumbnail.jpg"
err = resizeImage(tempFile.Name(), outputPath, int(req.MaxWidth), int(req.MaxHeight))
if err != nil {
return nil, err
}
case pb.FileType_VIDEO:
// Video file, use FFmpeg to create a thumbnail
outputPath = "thumbnails/video-thumbnail.jpg" // Video thumbnails are typically saved as JPG
err = generateVideoThumbnail(tempFile.Name(), outputPath, int(req.MaxWidth), int(req.MaxHeight))
if err != nil {
return nil, err
}
case pb.FileType_PDF:
// PDF file, use Poppler-utils to create a thumbnail
outputPath = "thumbnails/pdf-thumbnail.jpg" // PDF thumbnails are typically saved as JPG
err = generatePdfThumbnail(tempFile.Name(), outputPath, int(req.MaxWidth), int(req.MaxHeight))
if err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("unsupported file type: %v", req.FileType)
}
// Read the generated thumbnail back into memory to send it as bytes
if err != nil {
return nil, err
}
defer func() {
if _, err := os.Stat(outputPath); err == nil {
os.Remove(outputPath)
}
}()
thumbnailContent, err := os.ReadFile(outputPath)
if err != nil {
return nil, fmt.Errorf("failed to read generated thumbnail: %v", err)
}
// Return the response with the thumbnail bytes and output path
end := time.Since(time.Now())
fmt.Println(time.Now().Format("2006-01-02 15:04:05.000"), "Finshed in: ", end, req.FileType, "H: ", req.MaxHeight, "W: ", req.MaxWidth)
return &pb.ThumbnailResponse{
Message: "Thumbnail generated successfully",
ThumbnailContent: thumbnailContent, // Send the thumbnail as bytes
ThumbnailContent: thumbnailContent,
}, nil
}
func main() {
// Set up a listener on port 50051
listen, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("Failed to listen: %v", err)
}
// Create a gRPC server
grpcServer := grpc.NewServer()
// Register the server
pb.RegisterThumbnailServiceServer(grpcServer, &server{})
// Start serving requests
log.Println("Server started on port 50051")
if err := grpcServer.Serve(listen); err != nil {
log.Fatalf("Failed to serve: %v", err)