Files
learn-file-storage-s3-golan…/handler_upload_video.go
2025-03-15 23:35:24 +02:00

150 lines
3.5 KiB
Go

package main
import (
"bytes"
"context"
"crypto/rand"
"encoding/base64"
"fmt"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/bootdotdev/learn-file-storage-s3-golang-starter/internal/auth"
"github.com/google/uuid"
"io"
"mime"
"net/http"
"os"
)
func (cfg *apiConfig) handlerUploadVideo(w http.ResponseWriter, r *http.Request) {
videoIDString := r.PathValue("videoID")
videoID, err := uuid.Parse(videoIDString)
if err != nil {
respondWithError(w, http.StatusBadRequest, "Invalid ID", err)
return
}
token, err := auth.GetBearerToken(r.Header)
if err != nil {
respondWithError(w, http.StatusUnauthorized, "Couldn't find JWT", err)
return
}
userID, err := auth.ValidateJWT(token, cfg.jwtSecret)
if err != nil {
respondWithError(w, http.StatusUnauthorized, "Couldn't validate JWT", err)
return
}
fmt.Println("uploading video", videoID, "by user", userID)
var maxMemory int64
maxMemory = 10 << 30
err = r.ParseMultipartForm(maxMemory)
if err != nil {
respondWithError(w, http.StatusBadRequest, "Couldn't parse multipart form", err)
return
}
videoFile, header, err := r.FormFile("video")
defer videoFile.Close()
if err != nil {
respondWithError(w, http.StatusBadRequest, "Couldn't get file", err)
return
}
mediaType := header.Header.Get("Content-Type")
videoData, err := io.ReadAll(videoFile)
if err != nil {
respondWithError(w, http.StatusBadRequest, "Couldn't read video file", err)
return
}
video, err := cfg.db.GetVideo(videoID)
if err != nil {
respondWithError(w, http.StatusBadRequest, "Couldn't find video", err)
return
}
if video.UserID != userID {
respondWithError(w, http.StatusUnauthorized, "You are not authorized to upload this video", err)
return
}
var extension string
mimeType, _, err := mime.ParseMediaType(mediaType)
if err != nil {
respondWithError(w, http.StatusBadRequest, "Couldn't parse multipart form", err)
return
}
switch mimeType {
case "video/mp4":
extension = ".mp4"
break
default:
respondWithError(w, http.StatusBadRequest, "Couldn't recognize file type", err)
return
}
videoName := make([]byte, 32)
_, err = rand.Read(videoName)
if err != nil {
respondWithError(w, http.StatusBadRequest, "Couldn't generate random name", err)
return
}
videoPathName := base64.RawURLEncoding.EncodeToString(videoName)
tempFile, err := os.CreateTemp("", videoPathName)
if err != nil {
respondWithError(w, http.StatusBadRequest, "Couldn't create temp file", err)
return
}
defer os.Remove(videoPathName)
defer tempFile.Close()
_, err = io.Copy(tempFile, bytes.NewReader(videoData))
if err != nil {
respondWithError(w, http.StatusInternalServerError, "Couldn't create file", err)
return
}
tempFile.Seek(0, io.SeekStart)
fileName := videoPathName + extension
_, err = cfg.s3Client.PutObject(context.Background(), &s3.PutObjectInput{
Bucket: &cfg.s3Bucket,
Key: &fileName,
Body: tempFile,
ContentType: &mimeType,
})
if err != nil {
fmt.Printf("%s region, %s bucket", cfg.s3Region, cfg.s3Bucket)
respondWithError(w, http.StatusInternalServerError, "Couldn't upload video", err)
return
}
url := fmt.Sprintf("http://%s.s3.%s.amazonaws.com/%s", cfg.s3Bucket, cfg.s3Region, fileName)
video.VideoURL = &url
err = cfg.db.UpdateVideo(video)
if err != nil {
respondWithError(w, http.StatusBadRequest, "Couldn't update video", err)
return
}
video, err = cfg.db.GetVideo(videoID)
if err != nil {
respondWithError(w, http.StatusBadRequest, "Couldn't find video", err)
return
}
respondWithJSON(w, http.StatusOK, video)
}