diff --git a/fileuploading/fileuploading.go b/fileuploading/fileuploading.go new file mode 100644 index 0000000..658d2c1 --- /dev/null +++ b/fileuploading/fileuploading.go @@ -0,0 +1,105 @@ +package fileuploading + +//sudo apt-get install libvips-tools + +import ( + "encoding/base64" + "fmt" + "io/ioutil" + "log" + "net/http" + "os" + "path" + "personalwebsite/wde" + "personalwebsite/webfilesystem" + "strings" + + "github.com/disintegration/imaging" + "go.mongodb.org/mongo-driver/bson/primitive" +) + +type FileUploading struct { + fs *webfilesystem.WebFileSystem +} + +func NewFileUploading(webfs *webfilesystem.WebFileSystem) *FileUploading { + return &FileUploading{ + fs: webfs, + } +} + +func (f *FileUploading) CreateMiniatures(parentDir string, file string) error { + src, err := imaging.Open(path.Join(parentDir, file)) + if err != nil { + // log.Fatalf("failed to open image: %v", err) + return err + } + + // Resize the cropped image to width = 200px preserving the aspect ratio. + src = imaging.Resize(src, 32, 0, imaging.Lanczos) + + // Save the resulting image as JPEG. + splittedFileName := strings.Split(file, ".") + err = imaging.Save(src, path.Join(parentDir, splittedFileName[0]+"_32px."+splittedFileName[1])) + if err != nil { + // log.Fatalf("failed to save image: %v", err) + return err + } + + data, err := os.ReadFile(path.Join(parentDir, file)) + if err != nil { + return err + } + + min32Data, err := os.ReadFile(path.Join(parentDir, splittedFileName[0]+"_32px."+splittedFileName[1])) + if err != nil { + return err + } + imgData := wde.Img{ + Header: "", + Data: data, + Miniature32: min32Data, + } + newFile := webfilesystem.WebFSFile{ + MongoId: primitive.NewObjectID(), + Name: file, + Type: "picture", + Data: imgData, + Icon: "", + } + f.fs.CreateFile(&newFile, "/home/user/") + return nil +} + +func (f *FileUploading) EncodeToBase64(path string) (string, string) { + // Read the entire file into a byte slice + bytes, err := ioutil.ReadFile(path) + if err != nil { + log.Fatal(err) + } + + var base64Encoding string + + // Determine the content type of the image file + mimeType := http.DetectContentType(bytes) + + // Prepend the appropriate URI scheme header depending + // on the MIME type + switch mimeType { + case "image/jpeg": + base64Encoding += "image/jpeg;base64" + case "image/png": + base64Encoding += "image/png;base64" + } + + // Append the base64 encoded output + // base64Encoding += toBase64(bytes) + + // Print the full base64 representation of the image + fmt.Println(base64Encoding) + return base64Encoding, toBase64(bytes) +} + +func toBase64(b []byte) string { + return base64.StdEncoding.EncodeToString(b) +} diff --git a/go.mod b/go.mod index 1b644b7..d7a02a6 100644 --- a/go.mod +++ b/go.mod @@ -13,18 +13,21 @@ require ( github.com/xdg-go/scram v1.1.1 // indirect github.com/xdg-go/stringprep v1.0.3 // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect + golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 // indirect golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect ) require ( github.com/bytedance/sonic v1.8.0 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/disintegration/imaging v1.6.2 github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.11.2 // indirect github.com/goccy/go-json v0.10.0 // indirect github.com/google/uuid v1.3.0 + github.com/h2non/bimg v1.1.9 github.com/joho/godotenv v1.5.1 github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.0.9 // indirect @@ -33,6 +36,7 @@ require ( github.com/mitchellh/mapstructure v1.5.0 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.9 // indirect diff --git a/go.sum b/go.sum index 1de6cb5..e17d905 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhD github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= +github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8= @@ -26,6 +28,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/h2non/bimg v1.1.9 h1:WH20Nxko9l/HFm4kZCA3Phbgu2cbHvYzxwxn9YROEGg= +github.com/h2non/bimg v1.1.9/go.mod h1:R3+UiYwkK4rQl6KVFTOFJHitgLbZXBZNFh2cv3AEbp8= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -49,6 +53,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= +github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -83,6 +89,8 @@ golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUu golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE= golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU= +golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U= +golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= @@ -95,6 +103,7 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= diff --git a/main.go b/main.go index 0713d37..a0da8b7 100644 --- a/main.go +++ b/main.go @@ -3,9 +3,11 @@ package main import ( "context" "errors" + "fmt" "log" "net/http" "os" + "personalwebsite/fileuploading" "personalwebsite/routewde" "personalwebsite/wde" "personalwebsite/webfilesystem" @@ -56,15 +58,18 @@ func main() { router := gin.New() router.LoadHTMLGlob("templates/**/*") router.Static("/res", "resources") + // Set a lower memory limit for multipart forms (default is 32 MiB) + router.MaxMultipartMemory = 8 << 20 // 8 MiB router.GET("/", func(ctx *gin.Context) { ctx.HTML(http.StatusOK, "index.tmpl", gin.H{}) }) webfs := webfilesystem.NewWebFileSystem(client, dBName, webFsCollection) - wde := wde.NewWDE(webfs) - persPropsApp := personalprops.NewPersPropsApp(webfs) + webde := wde.NewWDE(webfs) + fileUploading := fileuploading.NewFileUploading(webfs) + persPropsApp := personalprops.NewPersPropsApp(webfs) // finderApp := finder.FinderApplication{} finderApp := finder.NewFinderApplication(webfs) imgViewerApp := imgviewer.NewImgViewerApp(webfs) @@ -76,12 +81,63 @@ func main() { appsStorage.Apps["finder"] = finderApp appsStorage.Apps["img-viewer"] = &imgViewerApp appsStorage.Apps["blog-viewer"] = blogViewerApp + + router.POST("/upload", func(c *gin.Context) { + // single file + file, _ := c.FormFile("file") + // log.Println(file.Filename) + + // Upload the file to specific dst. + dst := "./test-img/" + file.Filename + c.SaveUploadedFile(file, dst) + + fileUploading.CreateMiniatures("./test-img/", file.Filename) + + // webfs.CreateFile(&img, "/home/user/") + + c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename)) + }) + + router.GET("/testimg", func(c *gin.Context) { + + // c.Data(200, "image/jpeg", data) + }) + system := router.Group("system") { + imgLibGroup := system.Group("imglib") + { + imgLibGroup.GET("get", func(ctx *gin.Context) { + path := ctx.Query("path") + if path == "" { + ctx.JSON(http.StatusBadRequest, "TODO") //TODO json error struct + return + } + min := ctx.Query("min") + file, err := webfs.Read(path) + if err != nil { + ctx.JSON(http.StatusInternalServerError, "TODO") //TODO json error struct + return + } + + img, err := wde.ReadImage(file) + if err != nil { + ctx.JSON(http.StatusInternalServerError, "TODO") //TODO json error struct + return + } + + if min == "min32" { + ctx.Data(http.StatusOK, "image/jpeg", []byte(img.Miniature32)) + } else { + ctx.Data(http.StatusOK, "image/jpeg", []byte(img.Data)) + } + + }) + } wdeGroup := system.Group("wde") { - routewde.Route(wdeGroup, wde) + routewde.Route(wdeGroup, webde) } apps := system.Group("applications") { diff --git a/resources/sys/wde/file-view.css b/resources/sys/wde/file-view.css index 293210f..dc12843 100644 --- a/resources/sys/wde/file-view.css +++ b/resources/sys/wde/file-view.css @@ -42,7 +42,7 @@ .FileTileView .Icon{ width: 32px; height: 32px; - background-image: url("./icons/folder.png"); + /* background-image: url("./icons/folder.png"); */ background-size: cover; image-rendering: optimizeSpeed; /* STOP SMOOTHING, GIVE ME SPEED */ diff --git a/resources/wde.js b/resources/wde.js index 5357bbd..fc757a7 100644 --- a/resources/wde.js +++ b/resources/wde.js @@ -6,7 +6,7 @@ document.addEventListener('DOMContentLoaded', function() { if (!WebDesktopEnvironment.isMobile){ // WebDesktopEnvironment.Open("finder", ["/home/user"]) // WebDesktopEnvironment.Open("blog-viewer", ["/home/user/blog/test-1.blog"]) - WebDesktopEnvironment.Open("personal-properties", ["kek"]) + // WebDesktopEnvironment.Open("personal-properties", ["kek"]) } else { WebDesktopEnvironment.Open("blog-viewer", ["/home/user/blog/test-1.blog"]) } diff --git a/routewde/wde.go b/routewde/wde.go index 0e7a20b..ed37dbe 100644 --- a/routewde/wde.go +++ b/routewde/wde.go @@ -24,7 +24,7 @@ func Route(route *gin.RouterGroup, wde *wde.WDE) { ctx.JSON(http.StatusBadRequest, "TODO") //TODO json error struct return } - ginH, err := wde.Render(path) + ginH, err := wde.RenderFileTileView(path) if err != nil { ctx.JSON(http.StatusInternalServerError, "TODO") //TODO return diff --git a/templates/finder/app.tmpl b/templates/finder/app.tmpl index dabc9ee..8e47b24 100644 --- a/templates/finder/app.tmpl +++ b/templates/finder/app.tmpl @@ -1,5 +1,5 @@ {{ define "finder/app.tmpl" }} -
+
diff --git a/templates/img-viewer/app.tmpl b/templates/img-viewer/app.tmpl index e74126b..01a3f49 100644 --- a/templates/img-viewer/app.tmpl +++ b/templates/img-viewer/app.tmpl @@ -1,6 +1,6 @@ {{ define "img-viewer/app.tmpl" }} -
- +
+
About me diff --git a/templates/wde-widgets/file-tile-view.tmpl b/templates/wde-widgets/file-tile-view.tmpl index af81399..606eab2 100644 --- a/templates/wde-widgets/file-tile-view.tmpl +++ b/templates/wde-widgets/file-tile-view.tmpl @@ -1,7 +1,8 @@ {{ define "wde-widgets/file-tile-view.tmpl" }} {{ range $fileTile := .Files }}
-
+ +
{{ $fileTile.Name }}
{{ end }} diff --git a/test-img/cat2-test.jpeg b/test-img/cat2-test.jpeg new file mode 100644 index 0000000..1794a0a Binary files /dev/null and b/test-img/cat2-test.jpeg differ diff --git a/test-img/cat2-test_32px.jpeg b/test-img/cat2-test_32px.jpeg new file mode 100644 index 0000000..116d05d Binary files /dev/null and b/test-img/cat2-test_32px.jpeg differ diff --git a/test-img/cat2.jpeg b/test-img/cat2.jpeg new file mode 100644 index 0000000..1794a0a Binary files /dev/null and b/test-img/cat2.jpeg differ diff --git a/test-img/cat2_32px.jpeg b/test-img/cat2_32px.jpeg new file mode 100644 index 0000000..116d05d Binary files /dev/null and b/test-img/cat2_32px.jpeg differ diff --git a/test-img/out_example.jpg b/test-img/out_example.jpg new file mode 100644 index 0000000..fcf5016 --- /dev/null +++ b/test-img/out_example.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:91e19efe68edf356f518a435c84899d42d163d481cff4ed3192ddc82459c0813 +size 4750 diff --git a/wde/imglib.go b/wde/imglib.go index 6f549de..75baab9 100644 --- a/wde/imglib.go +++ b/wde/imglib.go @@ -7,23 +7,61 @@ import ( "go.mongodb.org/mongo-driver/bson/primitive" ) -func ReadImage(img *webfilesystem.WebFSFile) (*Base64Img, error) { - header, ok := img.Data.(primitive.D).Map()["header"].(string) - if !ok { - return nil, errors.New("error in file decoding") - } - base64, ok := img.Data.(primitive.D).Map()["base64"].(string) - if !ok { - return nil, errors.New("error in file decoding") - } +type ImagLib struct { + fs *webfilesystem.WebFileSystem +} - return &Base64Img{ - Header: header, - Base64: base64, +func ReadImage(img *webfilesystem.WebFSFile) (*Img, error) { + // header, ok := img.Data.(primitive.D).Map()["header"].(string) + // if !ok { + // return nil, errors.New("error in file decoding") + // } + // base64, ok := img.Data.(primitive.D).Map()["base64"].(string) + // if !ok { + // return nil, errors.New("error in file decoding") + // } + // base64min32, ok := img.Data.(primitive.D).Map()["base64min32"].(string) + // if !ok { + // return nil, errors.New("error in file decoding") + // } + data, ok := img.Data.(primitive.D).Map()["srcdata"] + if !ok { + return nil, errors.New("error in file decoding") + } + bin := data.(primitive.Binary).Data + + min32Data, ok := img.Data.(primitive.D).Map()["min32data"] + if !ok { + return nil, errors.New("error in file decoding") + } + min32Bin := min32Data.(primitive.Binary).Data + return &Img{ + // Header: header, + Data: bin, + Miniature32: min32Bin, }, nil } -type Base64Img struct { - Header string - Base64 string +func GetBase64Image(img *Base64Img, min string) (string, error) { + imgString := "" + switch min { + case "min32": + imgString = "data:" + img.Header + ", " + img.Base64Miniature32 + default: + imgString = "data:" + img.Header + ", " + img.Base64 + } + + return imgString, nil +} + +type Base64Img struct { + Header string `bson:"header"` + Base64 string `bson:"base64"` + Base64Miniature32 string `bson:"base64min32"` +} + +type Img struct { + Header string `bson:"header"` + Data []byte `bson:"srcdata"` + Miniature32 []byte `bson:"min32data"` } diff --git a/wde/wde.go b/wde/wde.go index 317133d..d45cb7f 100644 --- a/wde/wde.go +++ b/wde/wde.go @@ -30,3 +30,23 @@ func (w *WDE) Render(path string) (gin.H, error) { "Files": list, }, nil } + +func (w *WDE) RenderFileTileView(path string) (gin.H, error) { + list, err := w.fs.List(path) + if err != nil { + return nil, err + } + for _, file := range list { + switch file.Type { + case "directory": + file.Icon = "TODO" + case "base64img": + file.Icon = "http://localhost:8080/system/imglib/get?path=/home/user/cat2-test.jpeg&min=min32" + default: + file.Icon = "http://localhost:8080/system/imglib/get?path=/home/user/cat2-test.jpeg&min=min32" + } + } + return gin.H{ + "Files": list, + }, nil +} diff --git a/webfilesystem/webfilesystem.go b/webfilesystem/webfilesystem.go index 49f59be..dc06b2a 100644 --- a/webfilesystem/webfilesystem.go +++ b/webfilesystem/webfilesystem.go @@ -111,6 +111,7 @@ func (fs *WebFileSystem) CreateDirectory(path string) error { } func (fs *WebFileSystem) CreateFile(file *WebFSFile, parentPath string) error { + //TODO Check file existance parentDir, err := fs.Read(parentPath) if err != nil { return err @@ -169,6 +170,7 @@ type WebFSFile struct { Name string `bson:"name" json:"name"` Type string `bson:"type" json:"type"` Data interface{} `bson:"data" json:"-"` + Icon string `bson:"-" json:"icon"` } type FolderData struct { diff --git a/websiteapp/img-viewer/imgviewer.go b/websiteapp/img-viewer/imgviewer.go index 10fc7f8..5c1651f 100644 --- a/websiteapp/img-viewer/imgviewer.go +++ b/websiteapp/img-viewer/imgviewer.go @@ -42,6 +42,6 @@ func (p *ImgViewerApp) Render(path string, isMobile bool) (gin.H, error) { } return gin.H{ "header": data.Header, - "base64": data.Base64, + // "base64": data.Base64, }, nil } diff --git a/websiteapp/personalprops/personalprops.go b/websiteapp/personalprops/personalprops.go index b94719c..035cbca 100644 --- a/websiteapp/personalprops/personalprops.go +++ b/websiteapp/personalprops/personalprops.go @@ -147,19 +147,19 @@ func (p *PersonalPropertiesApp) Render() (gin.H, error) { return nil, errors.New("bad file") } headerProps := props.Data.(primitive.D).Map()["headerprops"].(primitive.D).Map() - file, err := p.fs.Read(headerProps["icon"].(string)) + // file, err := p.fs.Read(headerProps["icon"].(string)) if err != nil { //TODO err catch } - data, err := wde.ReadImage(file) - if err != nil { - return nil, err - } + // data, err := wde.ReadImage(file) + // if err != nil { + // return nil, err + // } hIsland := HeaderIsland{ - Name: headerProps["name"].(string), - Icon: data, + Name: headerProps["name"].(string), + // Icon: data, Info1: "LLL", Info2: "QQQ", }