Skip to content

Commit 38b3c4c

Browse files
committed
aws s3 implementaation
1 parent 6117cc5 commit 38b3c4c

16 files changed

Lines changed: 665 additions & 273 deletions

app/controller/object_controller.go

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,56 @@ package controller
22

33
import (
44
"fmt"
5+
"io"
56
"net/http"
6-
"os"
7+
"strconv"
78

9+
"github.com/Soup666/modelmaker/services"
810
"github.com/gin-gonic/gin"
911
)
1012

1113
// AuthController is the controller for handling authentication requests
12-
type ObjectController struct{}
14+
type ObjectController struct {
15+
storageService services.StorageService
16+
}
1317

14-
func NewObjectController() *ObjectController {
15-
return &ObjectController{}
18+
func NewObjectController(storageService services.StorageService) *ObjectController {
19+
return &ObjectController{
20+
storageService: storageService,
21+
}
1622
}
1723

1824
func (c *ObjectController) GetObject(ctx *gin.Context) {
1925
taskId := ctx.Param("taskID")
2026

21-
// Construct the full file path
22-
filePath := fmt.Sprintf("objects/%s/%s", taskId, "mvs/final_model.glb")
27+
// Convert taskId to uint
28+
taskIdInt, err := strconv.ParseUint(taskId, 10, 32)
29+
if err != nil {
30+
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid task ID"})
31+
return
32+
}
2333

24-
// Check if the file exists
25-
if _, err := os.Stat(filePath); os.IsNotExist(err) {
34+
// Get file from object storage
35+
file, err := c.storageService.GetFile(fmt.Sprintf("objects/%d/final.glb", taskIdInt))
36+
if err != nil {
2637
ctx.JSON(http.StatusNotFound, gin.H{"error": "Object not found"})
2738
return
2839
}
29-
30-
// Serve the file
31-
ctx.File(filePath)
40+
defer file.Close()
41+
42+
// Add headers
43+
ctx.Header("Content-Type", "model/gltf-binary")
44+
ctx.Header("Content-Disposition", "attachment; filename=final.glb")
45+
46+
// // Stream the file to the response
47+
// ctx.Stream(func(w io.Writer) bool {
48+
// _, err := io.Copy(w, file)
49+
// return err == nil
50+
// })
51+
// Copy file contents to response writer
52+
_, err = io.Copy(ctx.Writer, file)
53+
if err != nil {
54+
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to stream file"})
55+
return
56+
}
3257
}

app/controller/task_controller.go

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package controller
33
import (
44
"errors"
55
"fmt"
6+
"io"
67
"log"
78
"mime/multipart"
89
"net/http"
@@ -23,10 +24,16 @@ type TaskController struct {
2324
TaskService services.TaskService
2425
AppFileService services.AppFileService
2526
VisionService services.VisionService
27+
StorageService services.StorageService
2628
}
2729

28-
func NewTaskController(taskService services.TaskService, appFileService services.AppFileService, visionService services.VisionService) *TaskController {
29-
return &TaskController{TaskService: taskService, AppFileService: appFileService, VisionService: visionService}
30+
func NewTaskController(taskService services.TaskService, appFileService services.AppFileService, visionService services.VisionService, storageService services.StorageService) *TaskController {
31+
return &TaskController{
32+
TaskService: taskService,
33+
AppFileService: appFileService,
34+
VisionService: visionService,
35+
StorageService: storageService,
36+
}
3037
}
3138

3239
func (c *TaskController) GetUnarchivedTasks(ctx *gin.Context) {
@@ -136,7 +143,6 @@ func (c *TaskController) CreateTask(ctx *gin.Context) {
136143
// @Failure 500 {object} map[string]string
137144
// @Router /tasks/{taskID}/upload [post]
138145
func (c *TaskController) UploadFileToTask(ctx *gin.Context) {
139-
140146
// Get the Task ID from the route
141147
taskIdParam := ctx.Param("taskID")
142148
taskId, err := strconv.Atoi(taskIdParam)
@@ -164,13 +170,6 @@ func (c *TaskController) UploadFileToTask(ctx *gin.Context) {
164170
return
165171
}
166172

167-
// Define the upload folder
168-
folderPath := fmt.Sprintf("uploads/%d", taskId)
169-
if err := os.MkdirAll(folderPath, os.ModePerm); err != nil {
170-
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create upload directory"})
171-
return
172-
}
173-
174173
var uploadedImages []model.AppFile
175174
var wg sync.WaitGroup
176175
var mu sync.Mutex
@@ -192,21 +191,18 @@ func (c *TaskController) UploadFileToTask(ctx *gin.Context) {
192191
return
193192
}
194193

195-
// Generate a unique filename
196-
filename := fmt.Sprintf("%d-%d%s", taskId, index, fileExt)
197-
savePath := filepath.Join(folderPath, filename)
198-
199-
// Save the file
200-
if err := ctx.SaveUploadedFile(file, savePath); err != nil {
194+
// Upload to object storage
195+
url, err := c.StorageService.UploadFile(file, uint(taskId), "upload")
196+
if err != nil {
201197
ctx.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to save file %s", file.Filename)})
202198
hasError = true
203199
return
204200
}
205201

206202
// Save metadata to DB
207203
image := model.AppFile{
208-
Filename: filename,
209-
Url: fmt.Sprintf("/uploads/%d/%s", taskId, filename),
204+
Filename: file.Filename,
205+
Url: url,
210206
TaskId: uint(taskId),
211207
FileType: "upload",
212208
}
@@ -232,37 +228,42 @@ func (c *TaskController) UploadFileToTask(ctx *gin.Context) {
232228
tx.Commit()
233229

234230
go func() {
235-
// Generate caption
236-
result, err := c.VisionService.AnalyseImage(fmt.Sprintf("./uploads/%d/%s", taskId, uploadedImages[0].Filename), "")
231+
// Get the first image URL from object storage
232+
file, err := c.StorageService.GetFile(fmt.Sprintf("uploads/%d/%s", taskId, uploadedImages[0].Filename))
233+
if err != nil {
234+
log.Printf("Unable to get image for analysis: %v", err)
235+
return
236+
}
237+
defer file.Close()
237238

239+
// Create a temporary file
240+
tempFile, err := os.CreateTemp("", "analysis-*.jpg")
238241
if err != nil {
239-
log.Printf("Unable to analyze the image: %v", err)
242+
log.Printf("Unable to create temp file: %v", err)
240243
return
241244
}
245+
defer os.Remove(tempFile.Name())
246+
defer tempFile.Close()
242247

243-
if err := c.TaskService.UpdateMeta(&task, "ai-description", result); err != nil {
244-
log.Printf("Failed to update task metadata: %v", err)
248+
// Copy the file content
249+
if _, err := io.Copy(tempFile, file); err != nil {
250+
log.Printf("Unable to copy file content: %v", err)
251+
return
245252
}
246-
}()
247253

248-
go func() {
249254
// Generate caption
250-
result, err := c.VisionService.AnalyseImage(fmt.Sprintf("./uploads/%d/%s", taskId, uploadedImages[0].Filename), "categorize the model in this image, use one word only")
251-
255+
result, err := c.VisionService.AnalyseImage(tempFile.Name(), "")
252256
if err != nil {
253257
log.Printf("Unable to analyze the image: %v", err)
254258
return
255259
}
256260

257-
if err := c.TaskService.UpdateMeta(&task, "ai-title", result); err != nil {
261+
if err := c.TaskService.UpdateMeta(&task, "ai-description", result); err != nil {
258262
log.Printf("Failed to update task metadata: %v", err)
259263
}
260264
}()
261265

262-
ctx.JSON(http.StatusOK, gin.H{
263-
"message": "Files uploaded successfully",
264-
"images": uploadedImages,
265-
})
266+
ctx.JSON(http.StatusOK, gin.H{"message": "Files uploaded successfully", "images": uploadedImages})
266267
}
267268

268269
// StartProcess handles the process of starting the photogrammetry process
@@ -375,7 +376,7 @@ func (c *TaskController) SendMessage(ctx *gin.Context) {
375376
return
376377
}
377378

378-
imagePath := fmt.Sprintf("./uploads/%d/%s", taskId, task.Images[0].Filename)
379+
imagePath := fmt.Sprintf("uploads/%d/%s", taskId, task.Images[0].Filename)
379380

380381
if _, err := os.Stat(imagePath); os.IsNotExist(err) {
381382
log.Printf("Image file does not exist: %v\n", err)

app/controller/task_controller_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ func TestTaskController(t *testing.T) {
1717
mockTaskService := new(mocks.MockTaskService)
1818
mockAppFileService := new(mocks.MockAppFileService)
1919
mockVisionService := new(mocks.MockVisionService)
20+
mockStorageService := new(mocks.MockStorageService)
2021

21-
taskController := controller.NewTaskController(mockTaskService, mockAppFileService, mockVisionService)
22+
taskController := controller.NewTaskController(mockTaskService, mockAppFileService, mockVisionService, mockStorageService)
2223

2324
t.Run("GetUnarchivedTasks", func(t *testing.T) {
2425
recorder, c := utils.SetupRecorder()

app/controller/upload_controller.go

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,42 +4,36 @@ import (
44
"fmt"
55
"io"
66
"net/http"
7-
"os"
8-
"path/filepath"
7+
"strconv"
98

109
"github.com/Soup666/modelmaker/database"
1110
models "github.com/Soup666/modelmaker/model"
11+
"github.com/Soup666/modelmaker/services"
1212

1313
"github.com/gin-gonic/gin"
1414
)
1515

1616
// AuthController is the controller for handling authentication requests
1717
type UploadController struct {
18+
storageService services.StorageService
1819
}
1920

20-
func NewUploadController() *UploadController {
21-
return &UploadController{}
21+
func NewUploadController(storageService services.StorageService) *UploadController {
22+
return &UploadController{
23+
storageService: storageService,
24+
}
2225
}
2326

2427
func (c *UploadController) UploadFile(ctx *gin.Context) {
25-
2628
file, header, err := ctx.Request.FormFile("file")
2729
if err != nil {
2830
ctx.JSON(http.StatusBadRequest, gin.H{"error": "File upload failed"})
2931
return
3032
}
3133
defer file.Close()
3234

33-
// Save the file
34-
savePath := filepath.Join("uploads", header.Filename)
35-
out, err := os.Create(savePath)
36-
if err != nil {
37-
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Unable to save the file"})
38-
return
39-
}
40-
defer out.Close()
41-
42-
_, err = io.Copy(out, file)
35+
// Upload file to object storage
36+
url, err := c.storageService.UploadFile(header, 0, "upload")
4337
if err != nil {
4438
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Unable to save the file"})
4539
return
@@ -48,7 +42,7 @@ func (c *UploadController) UploadFile(ctx *gin.Context) {
4842
// Save file metadata in the database
4943
image := models.AppFile{
5044
Filename: header.Filename,
51-
Url: fmt.Sprintf("/%s", savePath),
45+
Url: url,
5246
}
5347
database.DB.Create(&image)
5448

@@ -59,31 +53,51 @@ func (c *UploadController) GetFile(ctx *gin.Context) {
5953
taskId := ctx.Param("taskId")
6054
filename := ctx.Param("filename")
6155

62-
// Construct the full file path
63-
filePath := fmt.Sprintf("uploads/%s/%s", taskId, filename)
56+
// Convert taskId to uint
57+
taskIdInt, err := strconv.ParseUint(taskId, 10, 32)
58+
if err != nil {
59+
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid task ID"})
60+
return
61+
}
6462

65-
// Check if the file exists
66-
if _, err := os.Stat(filePath); os.IsNotExist(err) {
67-
ctx.JSON(http.StatusNotFound, gin.H{"error": "Image not found", "path": filePath})
63+
// Get file from object storage
64+
file, err := c.storageService.GetFile(fmt.Sprintf("uploads/%d/%s", taskIdInt, filename))
65+
if err != nil {
66+
ctx.JSON(http.StatusNotFound, gin.H{"error": "Image not found"})
6867
return
6968
}
69+
defer file.Close()
7070

71-
// Serve the file
72-
ctx.File(filePath)
71+
// Stream the file directly to the response writer
72+
_, err = io.Copy(ctx.Writer, file)
73+
if err != nil {
74+
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to stream file"})
75+
return
76+
}
7377
}
7478

7579
func (c *UploadController) GetObject(ctx *gin.Context) {
7680
filename := ctx.Param("filename")
81+
taskId := ctx.Param("taskID")
7782

78-
// Construct the full file path
79-
filePath := filepath.Join("objects", filename)
83+
// Convert taskId to uint
84+
taskIdInt, err := strconv.ParseUint(taskId, 10, 32)
85+
if err != nil {
86+
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid task ID"})
87+
return
88+
}
8089

81-
// Check if the file exists
82-
if _, err := os.Stat(filePath); os.IsNotExist(err) {
90+
// Get file from object storage
91+
file, err := c.storageService.GetFile(fmt.Sprintf("objects/%d/%s", taskIdInt, filename))
92+
if err != nil {
8393
ctx.JSON(http.StatusNotFound, gin.H{"error": "Object not found"})
8494
return
8595
}
96+
defer file.Close()
8697

87-
// Serve the file
88-
ctx.File(filePath)
98+
// Stream the file to the response
99+
ctx.Stream(func(w io.Writer) bool {
100+
_, err := io.Copy(w, file)
101+
return err == nil
102+
})
89103
}

app/go.mod

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ require (
66
firebase.google.com/go/v4 v4.15.2
77
github.com/2024-dissertation/openmvgo v1.0.5
88
github.com/appleboy/go-fcm v1.2.5
9+
github.com/aws/aws-sdk-go-v2 v1.36.3
10+
github.com/aws/aws-sdk-go-v2/config v1.29.14
11+
github.com/aws/aws-sdk-go-v2/credentials v1.17.67
12+
github.com/aws/aws-sdk-go-v2/service/s3 v1.80.0
913
github.com/bodgit/sevenzip v1.6.1
1014
github.com/gin-gonic/gin v1.10.0
1115
github.com/google/generative-ai-go v0.19.0
@@ -37,6 +41,20 @@ require (
3741
github.com/KyleBanks/depth v1.2.1 // indirect
3842
github.com/MicahParks/keyfunc v1.9.0 // indirect
3943
github.com/andybalholm/brotli v1.1.1 // indirect
44+
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 // indirect
45+
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect
46+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect
47+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect
48+
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
49+
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 // indirect
50+
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect
51+
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.2 // indirect
52+
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect
53+
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 // indirect
54+
github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 // indirect
55+
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect
56+
github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 // indirect
57+
github.com/aws/smithy-go v1.22.2 // indirect
4058
github.com/bodgit/plumbing v1.3.0 // indirect
4159
github.com/bodgit/windows v1.0.1 // indirect
4260
github.com/bytedance/sonic v1.12.8 // indirect

0 commit comments

Comments
 (0)