diff --git a/backend/controller/postapi.go b/backend/controller/postapi.go index 62b70b5..7f103e9 100644 --- a/backend/controller/postapi.go +++ b/backend/controller/postapi.go @@ -118,10 +118,15 @@ func (pr *PostRouter) DownloadImage(c *gin.Context) { } key := c.Param("key") + _, isPreview := c.GetQuery("preview") buf := bytes.NewBuffer(nil) - err = pr.PostService.DownloadImage(key, buf) + if isPreview { + err = pr.PostService.PreviewImage(key, buf) + } else { + err = pr.PostService.DownloadImage(key, buf) + } if err != nil { pr.Fail(c, 1, err.Error()) diff --git a/backend/oss/base.go b/backend/oss/base.go index c900529..08ba0c7 100644 --- a/backend/oss/base.go +++ b/backend/oss/base.go @@ -4,6 +4,7 @@ import "io" type BaseOSSProvider interface { UploadFromIO(io.Reader, string) (string, error) + UploadFromIOWithKey(io.Reader, string, string) (string, error) DownloadToIO(string, io.Writer) error CheckObjectExist(string) (bool, error) } diff --git a/backend/oss/local.go b/backend/oss/local.go index 98178da..583bbef 100644 --- a/backend/oss/local.go +++ b/backend/oss/local.go @@ -29,18 +29,24 @@ func NewLocalStorage() *LocalStorage { func (l *LocalStorage) UploadFromIO(ioReader io.Reader, suffix string) (string, error) { objectName := generateObjectName() + key, err := l.UploadFromIOWithKey(ioReader, objectName, suffix) + + return key, err +} + +func (l *LocalStorage) UploadFromIOWithKey(ioReader io.Reader, key string, suffix string) (string, error) { if suffix != "" { - objectName += "." + suffix + key += "." + suffix } - file, err := os.Create(filepath.Join(l.dir, objectName)) + file, err := os.Create(filepath.Join(l.dir, key)) if err != nil { return "", err } defer file.Close() _, err = io.Copy(file, ioReader) - return objectName, err + return key, err } func (l *LocalStorage) DownloadToIO(objectName string, ioWriter io.Writer) error { diff --git a/backend/oss/minio.go b/backend/oss/minio.go index 785a24c..11cb601 100644 --- a/backend/oss/minio.go +++ b/backend/oss/minio.go @@ -43,15 +43,25 @@ func generateObjectName() string { // 从io.Reader上传文件 func (m *MinioClient) UploadFromIO(ioReader io.Reader, suffix string) (string, error) { - objectName := generateObjectName() + key, err := m.UploadFromIOWithKey(ioReader, objectName, suffix) + + return key, err +} + +func (m *MinioClient) UploadFromIOWithKey(ioReader io.Reader, key string, suffix string) (string, error) { if suffix != "" { - objectName += "." + suffix + key += "." + suffix + } + + _, err := m.client.PutObject(context.Background(), m.bucket, key, ioReader, -1, minio.PutObjectOptions{}) + + if err != nil { + return "", err } - _, err := m.client.PutObject(context.Background(), m.bucket, objectName, ioReader, -1, minio.PutObjectOptions{}) - return objectName, err + return key, nil } // 下载文件到io.Writer diff --git a/backend/service/post.go b/backend/service/post.go index d8caebc..def5880 100644 --- a/backend/service/post.go +++ b/backend/service/post.go @@ -1,8 +1,10 @@ package service import ( + "bytes" "errors" "io" + "strings" "github.com/RockChinQ/Campux/backend/database" "github.com/RockChinQ/Campux/backend/mq" @@ -40,6 +42,48 @@ func (ps *PostService) DownloadImage(key string, ioWriter io.Writer) error { return ps.OSS.DownloadToIO(key, ioWriter) } +func (ps *PostService) PreviewImage(key string, ioWriter io.Writer) error { + thumbnailKey := strings.Split(key, ".")[0] + "_thumbnail" + thumbnailKeySuffix := thumbnailKey + ".jpg" + + exist, err := ps.OSS.CheckObjectExist(thumbnailKeySuffix) + + if err != nil { + return err + } + + if exist { + // download the thumbnail directly + return ps.OSS.DownloadToIO(thumbnailKeySuffix, ioWriter) + } + + // compress the image and then upload. + + buf := new(bytes.Buffer) + err = ps.OSS.DownloadToIO(key, buf) + + if err != nil { + return err + } + + // compress the image + reader, err := util.CompressImage(buf.Bytes(), 10) + + if err != nil { + ioWriter.Write(buf.Bytes()) + return nil + } + + // CompressImage() should return a JPEG format image + _, err = ps.OSS.UploadFromIOWithKey(reader, thumbnailKey, "jpg") + + if err != nil { + return err + } + + return ps.OSS.DownloadToIO(thumbnailKeySuffix, ioWriter) +} + func (ps *PostService) PostNew(uuid string, uin int64, text string, images []string, anon bool) (int, error) { // 检查这个用户是否有未过审的帖子 diff --git a/backend/util/image.go b/backend/util/image.go new file mode 100644 index 0000000..478ddab --- /dev/null +++ b/backend/util/image.go @@ -0,0 +1,26 @@ +package util + +import ( + "bytes" + "image" + _ "image/gif" // register gif format + "image/jpeg" + _ "image/png" // register png format + "io" +) + +func CompressImage(input []byte, quality int) (io.Reader, error) { + img, _, err := image.Decode(bytes.NewReader(input)) + if err != nil { + return nil, err + } + + var buf bytes.Buffer + + err = jpeg.Encode(&buf, img, &jpeg.Options{Quality: quality}) + if err != nil { + return nil, err + } + + return bytes.NewReader(buf.Bytes()), nil +} diff --git a/frontend/src/pages/post.vue b/frontend/src/pages/post.vue index b35ff2e..1208a84 100644 --- a/frontend/src/pages/post.vue +++ b/frontend/src/pages/post.vue @@ -242,6 +242,8 @@ export default { // 将images中的baseurl去掉 this.post.images = this.post.images.map(image => { return image.replace(this.$store.state.base_url + "/v1/post/download-image/", '') + .replace("?preview=1", "") + }) this.$axios.post('/v1/post/post-new', this.post) @@ -286,7 +288,7 @@ export default { }) .then(res => { if (res.data.code === 0) { - let url = this.$store.state.base_url + '/v1/post/download-image/' + res.data.data.key + let url = this.$store.state.base_url + '/v1/post/download-image/' + res.data.data.key + "?preview=1" console.log(url) this.post.images.push(url) this.fetchLatestImageToImageBlobs(url) diff --git a/frontend/src/pages/world.vue b/frontend/src/pages/world.vue index e96fbc6..b85157a 100644 --- a/frontend/src/pages/world.vue +++ b/frontend/src/pages/world.vue @@ -197,7 +197,7 @@ export default { p[i].created_at = date.toLocaleString() p[i].status = this.$store.state.statusMap[p[i].status] for (let j = 0; j < p[i].images.length; j++) { - p[i].images[j] = this.$store.state.base_url + "/v1/post/download-image/" + p[i].images[j] + p[i].images[j] = this.$store.state.base_url + "/v1/post/download-image/" + p[i].images[j] + "?preview=1" } } console.log(p) @@ -250,7 +250,7 @@ export default { p[i].created_at = date.toLocaleString() p[i].status = this.$store.state.statusMap[p[i].status] for (let j = 0; j < p[i].images.length; j++) { - p[i].images[j] = this.$store.state.base_url + "/v1/post/download-image/" + p[i].images[j] + p[i].images[j] = this.$store.state.base_url + "/v1/post/download-image/" + p[i].images[j] + "?preview=1" } } console.log(p)