diff --git a/go.mod b/go.mod index 07afb5c..920cd2f 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( require ( github.com/dustin/go-humanize v1.0.1 // indirect + github.com/gabriel-vasile/mimetype v1.4.4 // indirect github.com/go-ini/ini v1.67.0 // indirect github.com/goccy/go-json v0.10.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect diff --git a/go.sum b/go.sum index 2f00a17..51ee3e9 100644 --- a/go.sum +++ b/go.sum @@ -7,6 +7,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I= +github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s= github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s= github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= diff --git a/server/files.go b/server/files.go index 52d678c..7199de7 100644 --- a/server/files.go +++ b/server/files.go @@ -12,6 +12,7 @@ import ( "strings" "github.com/danielgtaylor/huma/v2" + "github.com/gabriel-vasile/mimetype" "github.com/google/uuid" ) @@ -42,7 +43,15 @@ func Upload(ctx context.Context, input *UploadInput) (*UploadOutput, error) { return nil, err } - err = UploadToS3(ctx, fd, file_id.String(), file.Filename, file.Size) + det_buf := make([]byte, 1024) + n, err := fd.Read(det_buf) + if err != nil { + return nil, err + } + mime := mimetype.Detect(det_buf[:n]) + fd.Seek(0, 0) + + err = UploadToS3(ctx, fd, file_id.String(), file.Filename, file.Size, mime.String()) if err != nil { return nil, err } @@ -120,10 +129,18 @@ func Download(ctx context.Context, input *DownloadInput) (*huma.StreamResponse, stat, err := obj.Stat() filename := stat.UserMetadata["Filename"] + content_type := stat.UserMetadata["Type"] + + main_type, _, _ := strings.Cut(content_type, "/") ctx.SetHeader("Accept-Range", "bytes") ctx.SetHeader("Content-Length", fmt.Sprintf("%d", stat.Size)) - ctx.SetHeader("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename)) + if main_type == "text" { + ctx.SetHeader("Content-Disposition", fmt.Sprintf("inline; filename=%s", filename)) + } else { + ctx.SetHeader("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename)) + } + ctx.SetHeader("Content-Type", content_type) var start int64 var end int64 diff --git a/server/producer_test.go b/server/producer_test.go index 41e0af3..4f87217 100644 --- a/server/producer_test.go +++ b/server/producer_test.go @@ -12,6 +12,7 @@ import ( "testing" "github.com/danielgtaylor/huma/v2/humatest" + "github.com/gabriel-vasile/mimetype" ) func getTestAPI(t *testing.T) humatest.TestAPI { @@ -60,7 +61,8 @@ func uploadData(t *testing.T, api humatest.TestAPI, data []byte, filename string func TestUploadDownload(t *testing.T) { api := getTestAPI(t) - test_data := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9} + test_data := []byte("hello world") + mime := mimetype.Detect(test_data) filename := "test_file" upload_data := uploadData(t, api, test_data, filename) @@ -83,6 +85,15 @@ func TestUploadDownload(t *testing.T) { res := resp.Result() content_disposition_header := res.Header["Content-Disposition"][0] + content_type := res.Header["Content-Type"][0] + + if content_type == "" { + t.Fatal("Content-Type header is not set") + } + + if content_type != mime.String() { + t.Fatalf("Content-Type header is not the detected file type: %s != %s", content_type, mime.String()) + } matched, _ := regexp.MatchString( fmt.Sprintf("filename=%s", regexp.QuoteMeta(filename)), diff --git a/server/s3.go b/server/s3.go index e57abb8..f3fe7a3 100644 --- a/server/s3.go +++ b/server/s3.go @@ -19,7 +19,7 @@ func getS3Client() (*minio.Client, error) { return client, err } -func UploadToS3(ctx context.Context, file io.Reader, file_id string, filename string, filesize int64) error { +func UploadToS3(ctx context.Context, file io.Reader, file_id string, filename string, filesize int64, content_type string) error { client, err := getS3Client() if err != nil { return err @@ -31,7 +31,10 @@ func UploadToS3(ctx context.Context, file io.Reader, file_id string, filename st file, filesize, minio.PutObjectOptions{ - UserMetadata: map[string]string{"Filename": filename}, + UserMetadata: map[string]string{ + "Filename": filename, + "Type": content_type, + }, }, ) getLogger().Printf("upload info: %+v\n", info)