A:增加照片任务管理
This commit is contained in:
1 parent
f1d9764d1d
commit
25cc429091
11 files changed
+512
-126
No files matched your search
+2
-9
@@ -6,6 +6,7 @@ require (
|
|||||||
github.com/gin-contrib/cors v1.7.5
|
github.com/gin-contrib/cors v1.7.5
|
||||||
github.com/gin-contrib/pprof v1.5.3
|
github.com/gin-contrib/pprof v1.5.3
|
||||||
github.com/gin-gonic/gin v1.10.1
|
github.com/gin-gonic/gin v1.10.1
|
||||||
|
github.com/google/uuid v1.6.0
|
||||||
go.uber.org/zap v1.27.0
|
go.uber.org/zap v1.27.0
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||||
gorm.io/driver/mysql v1.6.0
|
gorm.io/driver/mysql v1.6.0
|
||||||
@@ -19,17 +20,13 @@ require (
|
|||||||
github.com/bytedance/sonic v1.13.3 // indirect
|
github.com/bytedance/sonic v1.13.3 // indirect
|
||||||
github.com/bytedance/sonic/loader v0.2.4 // indirect
|
github.com/bytedance/sonic/loader v0.2.4 // indirect
|
||||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
|
||||||
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
|
||||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||||
github.com/glebarez/go-sqlite v1.21.2 // indirect
|
|
||||||
github.com/glebarez/sqlite v1.11.0 // indirect
|
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.26.0 // indirect
|
github.com/go-playground/validator/v10 v10.26.0 // indirect
|
||||||
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||||
github.com/goccy/go-json v0.10.5 // indirect
|
github.com/goccy/go-json v0.10.5 // indirect
|
||||||
github.com/google/uuid v1.3.0 // indirect
|
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/jinzhu/now v1.1.5 // indirect
|
github.com/jinzhu/now v1.1.5 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
@@ -40,7 +37,6 @@ require (
|
|||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.14 // indirect
|
github.com/ugorji/go/codec v1.2.14 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
@@ -49,10 +45,7 @@ require (
|
|||||||
golang.org/x/net v0.41.0 // indirect
|
golang.org/x/net v0.41.0 // indirect
|
||||||
golang.org/x/sys v0.33.0 // indirect
|
golang.org/x/sys v0.33.0 // indirect
|
||||||
golang.org/x/text v0.26.0 // indirect
|
golang.org/x/text v0.26.0 // indirect
|
||||||
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||||
google.golang.org/protobuf v1.36.6 // indirect
|
google.golang.org/protobuf v1.36.6 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
modernc.org/libc v1.22.5 // indirect
|
|
||||||
modernc.org/mathutil v1.5.0 // indirect
|
|
||||||
modernc.org/memory v1.5.0 // indirect
|
|
||||||
modernc.org/sqlite v1.23.1 // indirect
|
|
||||||
)
|
)
|
||||||
+5
-23
@@ -11,8 +11,6 @@ github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQ
|
|||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
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/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.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
||||||
github.com/gin-contrib/cors v1.7.5 h1:cXC9SmofOrRg0w9PigwGlHG3ztswH6bqq4vJVXnvYMk=
|
github.com/gin-contrib/cors v1.7.5 h1:cXC9SmofOrRg0w9PigwGlHG3ztswH6bqq4vJVXnvYMk=
|
||||||
@@ -23,10 +21,6 @@ github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w
|
|||||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||||
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
|
github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ=
|
||||||
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
|
||||||
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
|
|
||||||
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
|
|
||||||
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
|
|
||||||
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
|
|
||||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
@@ -42,11 +36,10 @@ github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PU
|
|||||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
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.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
|
||||||
github.com/jinzhu/inflection v1.0.0+incompatible/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
|
||||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
@@ -74,9 +67,6 @@ github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0
|
|||||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
|
||||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
@@ -110,8 +100,8 @@ golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
|||||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
@@ -128,12 +118,4 @@ gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
|
|||||||
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
|
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
|
||||||
gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs=
|
gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs=
|
||||||
gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
|
gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
|
||||||
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
|
|
||||||
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
|
|
||||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
|
||||||
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
|
|
||||||
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
|
|
||||||
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
|
||||||
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
|
|
||||||
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
|
|
||||||
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package container
|
||||||
|
|
||||||
|
import (
|
||||||
|
"rear/internal/workflow"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TaskContainer struct {
|
||||||
|
// 照片任务处理管理
|
||||||
|
ImgTaskManager *workflow.ImgTaskManager
|
||||||
|
// 其他服务...
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTaskContainer() *TaskContainer {
|
||||||
|
return &TaskContainer{
|
||||||
|
ImgTaskManager: workflow.NewImgTaskManager(10),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,17 +6,19 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"rear/internal/container"
|
"rear/internal/container"
|
||||||
"rear/internal/model"
|
"rear/internal/model"
|
||||||
|
"rear/pkg/utils"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
type LibraryHandler struct {
|
type LibraryHandler struct {
|
||||||
container *container.Container
|
container *container.Container
|
||||||
|
imgContain *container.TaskContainer
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLibraryHandler(container *container.Container) *LibraryHandler {
|
func NewLibraryHandler(container *container.Container, imgContain *container.TaskContainer) *LibraryHandler {
|
||||||
return &LibraryHandler{container: container}
|
return &LibraryHandler{container: container, imgContain: imgContain}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLibrary 获取存储
|
// GetLibrary 获取存储
|
||||||
@@ -57,6 +59,7 @@ func (h *LibraryHandler) AddLibrary(c *gin.Context) {
|
|||||||
|
|
||||||
// 验证路径
|
// 验证路径
|
||||||
path := strings.TrimSpace(req.Path)
|
path := strings.TrimSpace(req.Path)
|
||||||
|
log.Printf("Add library path: %s", path)
|
||||||
if path == "" {
|
if path == "" {
|
||||||
c.JSON(http.StatusBadRequest, model.Response{
|
c.JSON(http.StatusBadRequest, model.Response{
|
||||||
Code: http.StatusBadRequest,
|
Code: http.StatusBadRequest,
|
||||||
@@ -139,3 +142,52 @@ func (h *LibraryHandler) DeleteLibrary(c *gin.Context) {
|
|||||||
Message: "Library deleted successfully",
|
Message: "Library deleted successfully",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LibraryIndex 开始图片检索【缩略图生成】
|
||||||
|
func (h *LibraryHandler) LibraryIndex(c *gin.Context) {
|
||||||
|
// 获取所有已添加路径
|
||||||
|
library, err := h.container.LibraryRepo.GetAllLibrary()
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, model.Response{
|
||||||
|
Code: http.StatusInternalServerError,
|
||||||
|
Message: err.Error(),
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(library) == 0 {
|
||||||
|
c.JSON(http.StatusInternalServerError, model.Response{
|
||||||
|
Code: http.StatusOK,
|
||||||
|
Message: "无可用路径, 任务取消!",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 存在可用路径,检索开始
|
||||||
|
var dirs []string // 创建一个空的 string 切片
|
||||||
|
for i := range library {
|
||||||
|
path := library[i]
|
||||||
|
isDir := utils.FileUtils.IsDir(path.ImgPath)
|
||||||
|
log.Printf(path.ImgPath)
|
||||||
|
if isDir {
|
||||||
|
dirs = append(dirs, path.ImgPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(dirs) == 0 {
|
||||||
|
c.JSON(http.StatusInternalServerError, model.Response{
|
||||||
|
Code: http.StatusOK,
|
||||||
|
Message: "无可用路径, 任务结束!",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取可用路径下所有的照片【指定类型】
|
||||||
|
//utils.FileUtils.GetAllFiles()
|
||||||
|
|
||||||
|
// 启动后台任务,开始处理
|
||||||
|
//h.imgContain.ImgTaskManager.AddTask()
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, model.Response{
|
||||||
|
Code: http.StatusOK,
|
||||||
|
Message: "索引任务已启动",
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// SetupRoutes 设置路由
|
// SetupRoutes 设置路由
|
||||||
func SetupRoutes(r *gin.Engine, contain *container.Container) {
|
func SetupRoutes(r *gin.Engine, contain *container.Container, imgContain *container.TaskContainer) {
|
||||||
// 默认访问
|
// 默认访问
|
||||||
r.GET("/", handler.BasicResponse)
|
r.GET("/", handler.BasicResponse)
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ func SetupRoutes(r *gin.Engine, contain *container.Container) {
|
|||||||
r.GET("/health", handler.HealthCheck)
|
r.GET("/health", handler.HealthCheck)
|
||||||
|
|
||||||
// 资料库处理
|
// 资料库处理
|
||||||
libraryHandler := handler.NewLibraryHandler(contain)
|
libraryHandler := handler.NewLibraryHandler(contain, imgContain)
|
||||||
devImageHandler := handler.NewDevImageHandler(contain)
|
devImageHandler := handler.NewDevImageHandler(contain)
|
||||||
// API版本组
|
// API版本组
|
||||||
v1 := r.Group("/api/v1")
|
v1 := r.Group("/api/v1")
|
||||||
@@ -38,6 +38,8 @@ func SetupRoutes(r *gin.Engine, contain *container.Container) {
|
|||||||
library.POST("", libraryHandler.AddLibrary)
|
library.POST("", libraryHandler.AddLibrary)
|
||||||
library.PUT("", libraryHandler.UpdateLibrary)
|
library.PUT("", libraryHandler.UpdateLibrary)
|
||||||
library.DELETE("", libraryHandler.DeleteLibrary)
|
library.DELETE("", libraryHandler.DeleteLibrary)
|
||||||
|
// 执行检索任务
|
||||||
|
library.POST("indexed", libraryHandler.LibraryIndex)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 开发组
|
// 开发组
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ func NewTaskScheduler(workers int, queueSize int) *TaskScheduler {
|
|||||||
ts.workerPool <- struct{}{}
|
ts.workerPool <- struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 启动worker
|
// 启动 worker
|
||||||
ts.startWorkers()
|
ts.startWorkers()
|
||||||
|
|
||||||
// 启动结果处理器
|
// 启动结果处理器
|
||||||
|
|||||||
@@ -0,0 +1,327 @@
|
|||||||
|
package workflow
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// --- 状态定义 ---
|
||||||
|
type TaskStatus string
|
||||||
|
|
||||||
|
const (
|
||||||
|
StatusPending TaskStatus = "pending"
|
||||||
|
StatusRunning TaskStatus = "running"
|
||||||
|
StatusPaused TaskStatus = "paused"
|
||||||
|
StatusFailed TaskStatus = "failed"
|
||||||
|
StatusDone TaskStatus = "done"
|
||||||
|
)
|
||||||
|
|
||||||
|
// --- PictureTask ---
|
||||||
|
type PictureTask struct {
|
||||||
|
ID string
|
||||||
|
Path string
|
||||||
|
Hash string
|
||||||
|
ImageBuf []byte
|
||||||
|
ExifData map[string]string
|
||||||
|
|
||||||
|
Status TaskStatus
|
||||||
|
Progress float64
|
||||||
|
Error error
|
||||||
|
|
||||||
|
ctx context.Context
|
||||||
|
cancel context.CancelFunc
|
||||||
|
mu sync.Mutex
|
||||||
|
pauseCh chan struct{}
|
||||||
|
resumeCh chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPictureTask(path string) *PictureTask {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
return &PictureTask{
|
||||||
|
ID: uuid.New().String(),
|
||||||
|
Path: path,
|
||||||
|
Status: StatusPending,
|
||||||
|
ctx: ctx,
|
||||||
|
cancel: cancel,
|
||||||
|
pauseCh: make(chan struct{}, 1),
|
||||||
|
resumeCh: make(chan struct{}, 1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pt *PictureTask) waitIfPaused() {
|
||||||
|
select {
|
||||||
|
case <-pt.pauseCh:
|
||||||
|
pt.setStatus(StatusPaused)
|
||||||
|
<-pt.resumeCh
|
||||||
|
pt.setStatus(StatusRunning)
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pt *PictureTask) Pause() {
|
||||||
|
select {
|
||||||
|
case pt.pauseCh <- struct{}{}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pt *PictureTask) Resume() {
|
||||||
|
select {
|
||||||
|
case pt.resumeCh <- struct{}{}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pt *PictureTask) Run() {
|
||||||
|
pt.setStatus(StatusRunning)
|
||||||
|
|
||||||
|
if !fileExists(pt.Path) {
|
||||||
|
pt.setError(errors.New("file does not exist"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pt.waitIfPaused()
|
||||||
|
data, err := os.ReadFile(pt.Path)
|
||||||
|
if err != nil {
|
||||||
|
pt.setError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pt.ImageBuf = data
|
||||||
|
pt.Hash = calcHash(data)
|
||||||
|
|
||||||
|
pt.waitIfPaused()
|
||||||
|
compressed, err := compressImage(data)
|
||||||
|
if err != nil {
|
||||||
|
pt.setError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pt.ImageBuf = compressed
|
||||||
|
|
||||||
|
pt.waitIfPaused()
|
||||||
|
exif, err := readExifViaCmd(pt.Path)
|
||||||
|
if err != nil {
|
||||||
|
pt.setError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pt.ExifData = exif
|
||||||
|
|
||||||
|
pt.waitIfPaused()
|
||||||
|
err = saveToDB(pt.Hash, exif)
|
||||||
|
if err != nil {
|
||||||
|
pt.setError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pt.setDone()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pt *PictureTask) setError(err error) {
|
||||||
|
pt.mu.Lock()
|
||||||
|
defer pt.mu.Unlock()
|
||||||
|
pt.Status = StatusFailed
|
||||||
|
pt.Error = err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pt *PictureTask) setDone() {
|
||||||
|
pt.mu.Lock()
|
||||||
|
defer pt.mu.Unlock()
|
||||||
|
pt.Status = StatusDone
|
||||||
|
pt.Progress = 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pt *PictureTask) setStatus(s TaskStatus) {
|
||||||
|
pt.mu.Lock()
|
||||||
|
defer pt.mu.Unlock()
|
||||||
|
pt.Status = s
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- ImgTaskManager ---
|
||||||
|
type ImgTaskManager struct {
|
||||||
|
tasks map[string]*PictureTask
|
||||||
|
queue chan *PictureTask
|
||||||
|
workerPool chan struct{}
|
||||||
|
workerLimit int
|
||||||
|
mu sync.RWMutex
|
||||||
|
globalPause chan struct{}
|
||||||
|
globalResume chan struct{}
|
||||||
|
poolMu sync.Mutex
|
||||||
|
doneCount int
|
||||||
|
autoAdjust bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewImgTaskManager(concurrency int) *ImgTaskManager {
|
||||||
|
tm := &ImgTaskManager{
|
||||||
|
tasks: make(map[string]*PictureTask),
|
||||||
|
queue: make(chan *PictureTask, 100),
|
||||||
|
workerPool: make(chan struct{}, concurrency),
|
||||||
|
workerLimit: concurrency,
|
||||||
|
globalPause: make(chan struct{}, 1),
|
||||||
|
globalResume: make(chan struct{}, 1),
|
||||||
|
autoAdjust: true,
|
||||||
|
}
|
||||||
|
go tm.run()
|
||||||
|
go tm.monitorCPU()
|
||||||
|
return tm
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tm *ImgTaskManager) SetConcurrency(n int) {
|
||||||
|
tm.poolMu.Lock()
|
||||||
|
defer tm.poolMu.Unlock()
|
||||||
|
|
||||||
|
if n <= 0 || n == tm.workerLimit {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
newPool := make(chan struct{}, n)
|
||||||
|
tm.mu.Lock()
|
||||||
|
used := len(tm.workerPool)
|
||||||
|
for i := 0; i < used && i < n; i++ {
|
||||||
|
newPool <- struct{}{}
|
||||||
|
}
|
||||||
|
tm.workerPool = newPool
|
||||||
|
tm.workerLimit = n
|
||||||
|
tm.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tm *ImgTaskManager) AddTask(path string) string {
|
||||||
|
task := NewPictureTask(path)
|
||||||
|
tm.mu.Lock()
|
||||||
|
tm.tasks[task.ID] = task
|
||||||
|
tm.mu.Unlock()
|
||||||
|
tm.queue <- task
|
||||||
|
return task.ID
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tm *ImgTaskManager) PauseAll() {
|
||||||
|
select {
|
||||||
|
case tm.globalPause <- struct{}{}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tm *ImgTaskManager) ResumeAll() {
|
||||||
|
select {
|
||||||
|
case tm.globalResume <- struct{}{}:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tm *ImgTaskManager) applyGlobalPauseResume(task *PictureTask) {
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-tm.globalPause:
|
||||||
|
task.Pause()
|
||||||
|
case <-tm.globalResume:
|
||||||
|
task.Resume()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tm *ImgTaskManager) run() {
|
||||||
|
for task := range tm.queue {
|
||||||
|
tm.workerPool <- struct{}{}
|
||||||
|
tm.applyGlobalPauseResume(task)
|
||||||
|
go func(t *PictureTask) {
|
||||||
|
defer func() { <-tm.workerPool }()
|
||||||
|
t.Run()
|
||||||
|
tm.mu.Lock()
|
||||||
|
if t.Status == StatusDone {
|
||||||
|
tm.doneCount++
|
||||||
|
}
|
||||||
|
tm.mu.Unlock()
|
||||||
|
}(task)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tm *ImgTaskManager) GetStatus(id string) TaskStatus {
|
||||||
|
tm.mu.RLock()
|
||||||
|
defer tm.mu.RUnlock()
|
||||||
|
if task, ok := tm.tasks[id]; ok {
|
||||||
|
return task.Status
|
||||||
|
}
|
||||||
|
return "not_found"
|
||||||
|
}
|
||||||
|
func (tm *ImgTaskManager) DoneCount() int {
|
||||||
|
tm.mu.RLock()
|
||||||
|
defer tm.mu.RUnlock()
|
||||||
|
return tm.doneCount
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tm *ImgTaskManager) RemainingCount() int {
|
||||||
|
tm.mu.RLock()
|
||||||
|
defer tm.mu.RUnlock()
|
||||||
|
return len(tm.tasks) - tm.doneCount
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tm *ImgTaskManager) monitorCPU() {
|
||||||
|
ticker := time.NewTicker(5 * time.Second)
|
||||||
|
for range ticker.C {
|
||||||
|
if !tm.autoAdjust {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cpuPercent := getCurrentCPUUsage()
|
||||||
|
if cpuPercent > 80 {
|
||||||
|
tm.SetConcurrency(tm.workerLimit / 2)
|
||||||
|
} else if cpuPercent < 40 {
|
||||||
|
tm.SetConcurrency(tm.workerLimit + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模拟 CPU 使用率函数(替换为真实库,如 gopsutil)
|
||||||
|
func getCurrentCPUUsage() int {
|
||||||
|
return runtime.NumGoroutine() * 5 // 简单估算
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 工具函数 ---
|
||||||
|
|
||||||
|
func fileExists(path string) bool {
|
||||||
|
_, err := os.Stat(path)
|
||||||
|
return !os.IsNotExist(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func calcHash(data []byte) string {
|
||||||
|
h := sha1.Sum(data)
|
||||||
|
return hex.EncodeToString(h[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func compressImage(data []byte) ([]byte, error) {
|
||||||
|
time.Sleep(100 * time.Millisecond) // 模拟耗时
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readExifViaCmd(filePath string) (map[string]string, error) {
|
||||||
|
cmd := exec.Command("exiftool", filePath)
|
||||||
|
out, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
result := make(map[string]string)
|
||||||
|
lines := bytes.Split(out, []byte("\n"))
|
||||||
|
for _, line := range lines {
|
||||||
|
kv := bytes.SplitN(line, []byte(":"), 2)
|
||||||
|
if len(kv) == 2 {
|
||||||
|
key := string(bytes.TrimSpace(kv[0]))
|
||||||
|
val := string(bytes.TrimSpace(kv[1]))
|
||||||
|
result[key] = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func saveToDB(hash string, exif map[string]string) error {
|
||||||
|
fmt.Printf("Saving %s to DB with %d exif fields\n", hash, len(exif))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
+7
-4
@@ -52,6 +52,9 @@ func main() {
|
|||||||
// 初始化基础服务(启动写操作处理协程)
|
// 初始化基础服务(启动写操作处理协程)
|
||||||
repositories.InitBaseService()
|
repositories.InitBaseService()
|
||||||
|
|
||||||
|
// 初始化照片管理任务
|
||||||
|
newTaskContainer := container.NewTaskContainer()
|
||||||
|
|
||||||
// 创建运行目录
|
// 创建运行目录
|
||||||
execPath, err := os.Executable()
|
execPath, err := os.Executable()
|
||||||
execDir := filepath.Dir(execPath)
|
execDir := filepath.Dir(execPath)
|
||||||
@@ -61,7 +64,7 @@ func main() {
|
|||||||
srcDir := `.\tools\windows_amd64\exiftool\exiftool` // 源目录
|
srcDir := `.\tools\windows_amd64\exiftool\exiftool` // 源目录
|
||||||
|
|
||||||
// 复制整个目录
|
// 复制整个目录
|
||||||
if err := utils.CopyDir(srcDir, join); err != nil {
|
if err := utils.FileUtils.CopyDir(srcDir, join); err != nil {
|
||||||
fmt.Printf("复制失败: %v\n", err)
|
fmt.Printf("复制失败: %v\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -72,10 +75,10 @@ func main() {
|
|||||||
// 依赖注入
|
// 依赖注入
|
||||||
|
|
||||||
// 启动 http
|
// 启动 http
|
||||||
startHttp(newContainer)
|
startHttp(newContainer, newTaskContainer)
|
||||||
}
|
}
|
||||||
|
|
||||||
func startHttp(con *container.Container) {
|
func startHttp(con *container.Container, imgContain *container.TaskContainer) {
|
||||||
// 配置加载
|
// 配置加载
|
||||||
netConfig := config.InitConfig()
|
netConfig := config.InitConfig()
|
||||||
// 设置Gin模式
|
// 设置Gin模式
|
||||||
@@ -103,7 +106,7 @@ func startHttp(con *container.Container) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 设置路由
|
// 设置路由
|
||||||
router.SetupRoutes(r, con)
|
router.SetupRoutes(r, con, imgContain)
|
||||||
|
|
||||||
// 创建HTTP服务器
|
// 创建HTTP服务器
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
|
|||||||
@@ -22,14 +22,20 @@ type FileInfo struct {
|
|||||||
Ext string `json:"ext"`
|
Ext string `json:"ext"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fileUtilsStruct 用于封装文件工具方法
|
||||||
|
type fileUtilsStruct struct{}
|
||||||
|
|
||||||
|
// FileUtils 是对外暴露的文件工具对象
|
||||||
|
var FileUtils = fileUtilsStruct{}
|
||||||
|
|
||||||
// 1. 检查文件或目录是否存在
|
// 1. 检查文件或目录是否存在
|
||||||
func Exists(path string) bool {
|
func (fileUtilsStruct) Exists(path string) bool {
|
||||||
_, err := os.Stat(path)
|
_, err := os.Stat(path)
|
||||||
return !os.IsNotExist(err)
|
return !os.IsNotExist(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 检查是否为目录
|
// 2. 检查是否为目录
|
||||||
func IsDir(path string) bool {
|
func (fileUtilsStruct) IsDir(path string) bool {
|
||||||
info, err := os.Stat(path)
|
info, err := os.Stat(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
@@ -38,7 +44,7 @@ func IsDir(path string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3. 检查是否为文件
|
// 3. 检查是否为文件
|
||||||
func IsFile(path string) bool {
|
func (fileUtilsStruct) IsFile(path string) bool {
|
||||||
info, err := os.Stat(path)
|
info, err := os.Stat(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
@@ -47,7 +53,7 @@ func IsFile(path string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 4. 获取指定目录下的所有文件夹
|
// 4. 获取指定目录下的所有文件夹
|
||||||
func GetDirectories(dirPath string) ([]string, error) {
|
func (fileUtilsStruct) GetDirectories(dirPath string) ([]string, error) {
|
||||||
var dirs []string
|
var dirs []string
|
||||||
|
|
||||||
entries, err := os.ReadDir(dirPath)
|
entries, err := os.ReadDir(dirPath)
|
||||||
@@ -65,7 +71,7 @@ func GetDirectories(dirPath string) ([]string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 5. 获取指定目录下指定格式的文件
|
// 5. 获取指定目录下指定格式的文件
|
||||||
func GetFilesByExtension(dirPath string, extensions ...string) ([]string, error) {
|
func (fileUtilsStruct) GetFilesByExtension(dirPath string, extensions ...string) ([]string, error) {
|
||||||
var files []string
|
var files []string
|
||||||
|
|
||||||
// 标准化扩展名(确保以.开头)
|
// 标准化扩展名(确保以.开头)
|
||||||
@@ -97,8 +103,8 @@ func GetFilesByExtension(dirPath string, extensions ...string) ([]string, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 6. 找到指定目录下第一个符合格式的文件
|
// 6. 找到指定目录下第一个符合格式的文件
|
||||||
func GetFirstFileByExtension(dirPath string, extensions ...string) (string, error) {
|
func (f fileUtilsStruct) GetFirstFileByExtension(dirPath string, extensions ...string) (string, error) {
|
||||||
files, err := GetFilesByExtension(dirPath, extensions...)
|
files, err := f.GetFilesByExtension(dirPath, extensions...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -111,7 +117,7 @@ func GetFirstFileByExtension(dirPath string, extensions ...string) (string, erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 7. 递归获取目录下所有文件
|
// 7. 递归获取目录下所有文件
|
||||||
func GetAllFiles(dirPath string, recursive bool) ([]FileInfo, error) {
|
func (fileUtilsStruct) GetAllFiles(dirPath string, recursive bool) ([]FileInfo, error) {
|
||||||
var files []FileInfo
|
var files []FileInfo
|
||||||
|
|
||||||
if recursive {
|
if recursive {
|
||||||
@@ -161,7 +167,7 @@ func GetAllFiles(dirPath string, recursive bool) ([]FileInfo, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 递归获取目录下所有文件夹
|
// 递归获取目录下所有文件夹
|
||||||
func GetAllDirs(dirPath string, recursive bool) ([]FileInfo, error) {
|
func (fileUtilsStruct) GetAllDirs(dirPath string, recursive bool) ([]FileInfo, error) {
|
||||||
var dirs []FileInfo
|
var dirs []FileInfo
|
||||||
|
|
||||||
if recursive {
|
if recursive {
|
||||||
@@ -212,8 +218,8 @@ func GetAllDirs(dirPath string, recursive bool) ([]FileInfo, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 8. 删除文件或目录
|
// 8. 删除文件或目录
|
||||||
func Delete(path string) error {
|
func (f fileUtilsStruct) Delete(path string) error {
|
||||||
if !Exists(path) {
|
if !f.Exists(path) {
|
||||||
return fmt.Errorf("路径不存在: %s", path)
|
return fmt.Errorf("路径不存在: %s", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,12 +227,12 @@ func Delete(path string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 9. 创建目录(递归创建)
|
// 9. 创建目录(递归创建)
|
||||||
func CreateDir(dirPath string) error {
|
func (fileUtilsStruct) CreateDir(dirPath string) error {
|
||||||
return os.MkdirAll(dirPath, 0755)
|
return os.MkdirAll(dirPath, 0755)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 10. 复制文件
|
// 10. 复制文件
|
||||||
func CopyFile(src, dst string) error {
|
func (f fileUtilsStruct) CopyFile(src, dst string) error {
|
||||||
sourceFile, err := os.Open(src)
|
sourceFile, err := os.Open(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("打开源文件失败: %w", err)
|
return fmt.Errorf("打开源文件失败: %w", err)
|
||||||
@@ -241,7 +247,7 @@ func CopyFile(src, dst string) error {
|
|||||||
|
|
||||||
// 确保目标目录存在
|
// 确保目标目录存在
|
||||||
dstDir := filepath.Dir(dst)
|
dstDir := filepath.Dir(dst)
|
||||||
if err := CreateDir(dstDir); err != nil {
|
if err := f.CreateDir(dstDir); err != nil {
|
||||||
return fmt.Errorf("创建目标目录失败: %w", err)
|
return fmt.Errorf("创建目标目录失败: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,7 +267,7 @@ func CopyFile(src, dst string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CopyDir 递归复制整个目录
|
// CopyDir 递归复制整个目录
|
||||||
func CopyDir(src, dst string) error {
|
func (f fileUtilsStruct) CopyDir(src, dst string) error {
|
||||||
// 获取源目录信息
|
// 获取源目录信息
|
||||||
srcInfo, err := os.Stat(src)
|
srcInfo, err := os.Stat(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -291,12 +297,12 @@ func CopyDir(src, dst string) error {
|
|||||||
|
|
||||||
if entry.IsDir() {
|
if entry.IsDir() {
|
||||||
// 如果是目录,递归复制
|
// 如果是目录,递归复制
|
||||||
if err := CopyDir(srcPath, dstPath); err != nil {
|
if err := f.CopyDir(srcPath, dstPath); err != nil {
|
||||||
return fmt.Errorf("复制子目录 %s 失败: %w", entry.Name(), err)
|
return fmt.Errorf("复制子目录 %s 失败: %w", entry.Name(), err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 如果是文件,使用你的 CopyFile 函数
|
// 如果是文件,使用你的 CopyFile 函数
|
||||||
if err := CopyFile(srcPath, dstPath); err != nil {
|
if err := f.CopyFile(srcPath, dstPath); err != nil {
|
||||||
return fmt.Errorf("复制文件 %s 失败: %w", entry.Name(), err)
|
return fmt.Errorf("复制文件 %s 失败: %w", entry.Name(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -311,7 +317,7 @@ type CopyOptions struct {
|
|||||||
SkipSymlinks bool // 是否跳过符号链接
|
SkipSymlinks bool // 是否跳过符号链接
|
||||||
}
|
}
|
||||||
|
|
||||||
func CopyDirWithOptions(src, dst string, opts *CopyOptions) error {
|
func (f fileUtilsStruct) CopyDirWithOptions(src, dst string, opts *CopyOptions) error {
|
||||||
if opts == nil {
|
if opts == nil {
|
||||||
opts = &CopyOptions{Overwrite: true, SkipSymlinks: false}
|
opts = &CopyOptions{Overwrite: true, SkipSymlinks: false}
|
||||||
}
|
}
|
||||||
@@ -358,7 +364,7 @@ func CopyDirWithOptions(src, dst string, opts *CopyOptions) error {
|
|||||||
if opts.SkipSymlinks {
|
if opts.SkipSymlinks {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := copySymlink(srcPath, dstPath); err != nil {
|
if err := f.copySymlink(srcPath, dstPath); err != nil {
|
||||||
return fmt.Errorf("复制符号链接 %s 失败: %w", entry.Name(), err)
|
return fmt.Errorf("复制符号链接 %s 失败: %w", entry.Name(), err)
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
@@ -366,7 +372,7 @@ func CopyDirWithOptions(src, dst string, opts *CopyOptions) error {
|
|||||||
|
|
||||||
if entry.IsDir() {
|
if entry.IsDir() {
|
||||||
// 递归复制子目录
|
// 递归复制子目录
|
||||||
if err := CopyDirWithOptions(srcPath, dstPath, opts); err != nil {
|
if err := f.CopyDirWithOptions(srcPath, dstPath, opts); err != nil {
|
||||||
return fmt.Errorf("复制子目录 %s 失败: %w", entry.Name(), err)
|
return fmt.Errorf("复制子目录 %s 失败: %w", entry.Name(), err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -378,7 +384,7 @@ func CopyDirWithOptions(src, dst string, opts *CopyOptions) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 复制文件
|
// 复制文件
|
||||||
if err := CopyFile(srcPath, dstPath); err != nil {
|
if err := f.CopyFile(srcPath, dstPath); err != nil {
|
||||||
return fmt.Errorf("复制文件 %s 失败: %w", entry.Name(), err)
|
return fmt.Errorf("复制文件 %s 失败: %w", entry.Name(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -388,7 +394,7 @@ func CopyDirWithOptions(src, dst string, opts *CopyOptions) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// copySymlink 复制符号链接
|
// copySymlink 复制符号链接
|
||||||
func copySymlink(src, dst string) error {
|
func (fileUtilsStruct) copySymlink(src, dst string) error {
|
||||||
link, err := os.Readlink(src)
|
link, err := os.Readlink(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -401,10 +407,10 @@ func copySymlink(src, dst string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 11. 移动/重命名文件
|
// 11. 移动/重命名文件
|
||||||
func MoveFile(src, dst string) error {
|
func (f fileUtilsStruct) MoveFile(src, dst string) error {
|
||||||
// 确保目标目录存在
|
// 确保目标目录存在
|
||||||
dstDir := filepath.Dir(dst)
|
dstDir := filepath.Dir(dst)
|
||||||
if err := CreateDir(dstDir); err != nil {
|
if err := f.CreateDir(dstDir); err != nil {
|
||||||
return fmt.Errorf("创建目标目录失败: %w", err)
|
return fmt.Errorf("创建目标目录失败: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,7 +418,7 @@ func MoveFile(src, dst string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 12. 读取文件全部内容
|
// 12. 读取文件全部内容
|
||||||
func ReadFile(filePath string) (string, error) {
|
func (fileUtilsStruct) ReadFile(filePath string) (string, error) {
|
||||||
content, err := os.ReadFile(filePath)
|
content, err := os.ReadFile(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("读取文件失败: %w", err)
|
return "", fmt.Errorf("读取文件失败: %w", err)
|
||||||
@@ -421,7 +427,7 @@ func ReadFile(filePath string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 13. 按行读取文件
|
// 13. 按行读取文件
|
||||||
func ReadFileLines(filePath string) ([]string, error) {
|
func (fileUtilsStruct) ReadFileLines(filePath string) ([]string, error) {
|
||||||
file, err := os.Open(filePath)
|
file, err := os.Open(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("打开文件失败: %w", err)
|
return nil, fmt.Errorf("打开文件失败: %w", err)
|
||||||
@@ -442,10 +448,10 @@ func ReadFileLines(filePath string) ([]string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 14. 写入文件内容
|
// 14. 写入文件内容
|
||||||
func WriteFile(filePath, content string) error {
|
func (f fileUtilsStruct) WriteFile(filePath, content string) error {
|
||||||
// 确保目录存在
|
// 确保目录存在
|
||||||
dir := filepath.Dir(filePath)
|
dir := filepath.Dir(filePath)
|
||||||
if err := CreateDir(dir); err != nil {
|
if err := f.CreateDir(dir); err != nil {
|
||||||
return fmt.Errorf("创建目录失败: %w", err)
|
return fmt.Errorf("创建目录失败: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -453,10 +459,10 @@ func WriteFile(filePath, content string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 15. 追加写入文件
|
// 15. 追加写入文件
|
||||||
func AppendFile(filePath, content string) error {
|
func (f fileUtilsStruct) AppendFile(filePath, content string) error {
|
||||||
// 确保目录存在
|
// 确保目录存在
|
||||||
dir := filepath.Dir(filePath)
|
dir := filepath.Dir(filePath)
|
||||||
if err := CreateDir(dir); err != nil {
|
if err := f.CreateDir(dir); err != nil {
|
||||||
return fmt.Errorf("创建目录失败: %w", err)
|
return fmt.Errorf("创建目录失败: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -471,7 +477,7 @@ func AppendFile(filePath, content string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 16. 获取文件大小
|
// 16. 获取文件大小
|
||||||
func GetFileSize(filePath string) (int64, error) {
|
func (fileUtilsStruct) GetFileSize(filePath string) (int64, error) {
|
||||||
info, err := os.Stat(filePath)
|
info, err := os.Stat(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("获取文件信息失败: %w", err)
|
return 0, fmt.Errorf("获取文件信息失败: %w", err)
|
||||||
@@ -480,7 +486,7 @@ func GetFileSize(filePath string) (int64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 17. 获取文件修改时间
|
// 17. 获取文件修改时间
|
||||||
func GetModTime(filePath string) (time.Time, error) {
|
func (fileUtilsStruct) GetModTime(filePath string) (time.Time, error) {
|
||||||
info, err := os.Stat(filePath)
|
info, err := os.Stat(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return time.Time{}, fmt.Errorf("获取文件信息失败: %w", err)
|
return time.Time{}, fmt.Errorf("获取文件信息失败: %w", err)
|
||||||
@@ -489,29 +495,29 @@ func GetModTime(filePath string) (time.Time, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 18. 获取文件扩展名
|
// 18. 获取文件扩展名
|
||||||
func GetExtension(filePath string) string {
|
func (fileUtilsStruct) GetExtension(filePath string) string {
|
||||||
return filepath.Ext(filePath)
|
return filepath.Ext(filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 19. 获取文件名(不含扩展名)
|
// 19. 获取文件名(不含扩展名)
|
||||||
func GetFileName(filePath string) string {
|
func (fileUtilsStruct) GetFileName(filePath string) string {
|
||||||
base := filepath.Base(filePath)
|
base := filepath.Base(filePath)
|
||||||
ext := filepath.Ext(base)
|
ext := filepath.Ext(base)
|
||||||
return strings.TrimSuffix(base, ext)
|
return strings.TrimSuffix(base, ext)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 20. 获取文件基本名称(含扩展名)
|
// 20. 获取文件基本名称(含扩展名)
|
||||||
func GetBaseName(filePath string) string {
|
func (fileUtilsStruct) GetBaseName(filePath string) string {
|
||||||
return filepath.Base(filePath)
|
return filepath.Base(filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 21. 获取文件目录
|
// 21. 获取文件目录
|
||||||
func GetDir(filePath string) string {
|
func (fileUtilsStruct) GetDir(filePath string) string {
|
||||||
return filepath.Dir(filePath)
|
return filepath.Dir(filePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 22. 计算文件MD5哈希值
|
// 22. 计算文件MD5哈希值
|
||||||
func GetFileMD5(filePath string) (string, error) {
|
func (fileUtilsStruct) GetFileMD5(filePath string) (string, error) {
|
||||||
file, err := os.Open(filePath)
|
file, err := os.Open(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("打开文件失败: %w", err)
|
return "", fmt.Errorf("打开文件失败: %w", err)
|
||||||
@@ -527,7 +533,7 @@ func GetFileMD5(filePath string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 23. 计算文件SHA256哈希值
|
// 23. 计算文件SHA256哈希值
|
||||||
func GetFileSHA256(filePath string) (string, error) {
|
func (fileUtilsStruct) GetFileSHA256(filePath string) (string, error) {
|
||||||
file, err := os.Open(filePath)
|
file, err := os.Open(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("打开文件失败: %w", err)
|
return "", fmt.Errorf("打开文件失败: %w", err)
|
||||||
@@ -543,7 +549,7 @@ func GetFileSHA256(filePath string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 24. 清空目录内容(保留目录本身)
|
// 24. 清空目录内容(保留目录本身)
|
||||||
func CleanDir(dirPath string) error {
|
func (fileUtilsStruct) CleanDir(dirPath string) error {
|
||||||
entries, err := os.ReadDir(dirPath)
|
entries, err := os.ReadDir(dirPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("读取目录失败: %w", err)
|
return fmt.Errorf("读取目录失败: %w", err)
|
||||||
@@ -560,7 +566,7 @@ func CleanDir(dirPath string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 25. 获取目录大小
|
// 25. 获取目录大小
|
||||||
func GetDirSize(dirPath string) (int64, error) {
|
func (fileUtilsStruct) GetDirSize(dirPath string) (int64, error) {
|
||||||
var size int64
|
var size int64
|
||||||
|
|
||||||
err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
|
err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
|
||||||
@@ -577,7 +583,7 @@ func GetDirSize(dirPath string) (int64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 26. 格式化文件大小
|
// 26. 格式化文件大小
|
||||||
func FormatFileSize(bytes int64) string {
|
func (fileUtilsStruct) FormatFileSize(bytes int64) string {
|
||||||
const unit = 1024
|
const unit = 1024
|
||||||
if bytes < unit {
|
if bytes < unit {
|
||||||
return fmt.Sprintf("%d B", bytes)
|
return fmt.Sprintf("%d B", bytes)
|
||||||
@@ -591,7 +597,7 @@ func FormatFileSize(bytes int64) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 27. 搜索文件(按文件名模式)
|
// 27. 搜索文件(按文件名模式)
|
||||||
func SearchFiles(dirPath, pattern string, recursive bool) ([]string, error) {
|
func (fileUtilsStruct) SearchFiles(dirPath, pattern string, recursive bool) ([]string, error) {
|
||||||
var matches []string
|
var matches []string
|
||||||
|
|
||||||
if recursive {
|
if recursive {
|
||||||
@@ -634,11 +640,11 @@ func SearchFiles(dirPath, pattern string, recursive bool) ([]string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 28. 获取当前工作目录
|
// 28. 获取当前工作目录
|
||||||
func GetCurrentDir() (string, error) {
|
func (fileUtilsStruct) GetCurrentDir() (string, error) {
|
||||||
return os.Getwd()
|
return os.Getwd()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 29. 改变工作目录
|
// 29. 改变工作目录
|
||||||
func ChangeDir(dirPath string) error {
|
func (fileUtilsStruct) ChangeDir(dirPath string) error {
|
||||||
return os.Chdir(dirPath)
|
return os.Chdir(dirPath)
|
||||||
}
|
}
|
||||||
@@ -14,6 +14,10 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type hashUtilsStruct struct{}
|
||||||
|
|
||||||
|
var HashUtils = hashUtilsStruct{}
|
||||||
|
|
||||||
// HashType 定义支持的Hash算法类型
|
// HashType 定义支持的Hash算法类型
|
||||||
type HashType int
|
type HashType int
|
||||||
|
|
||||||
@@ -40,7 +44,7 @@ type HashResult struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getHasher 根据类型返回对应的Hash实例
|
// getHasher 根据类型返回对应的Hash实例
|
||||||
func getHasher(hashType HashType) hash.Hash {
|
func (h hashUtilsStruct) getHasher(hashType HashType) hash.Hash {
|
||||||
switch hashType {
|
switch hashType {
|
||||||
case MD5:
|
case MD5:
|
||||||
return md5.New()
|
return md5.New()
|
||||||
@@ -56,7 +60,7 @@ func getHasher(hashType HashType) hash.Hash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getHasherName 获取Hash算法名称
|
// getHasherName 获取Hash算法名称
|
||||||
func getHasherName(hashType HashType) string {
|
func (h hashUtilsStruct) getHasherName(hashType HashType) string {
|
||||||
switch hashType {
|
switch hashType {
|
||||||
case MD5:
|
case MD5:
|
||||||
return "MD5"
|
return "MD5"
|
||||||
@@ -72,21 +76,21 @@ func getHasherName(hashType HashType) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HashString 计算字符串的Hash值
|
// HashString 计算字符串的Hash值
|
||||||
func HashString(data string, hashType HashType) string {
|
func (h hashUtilsStruct) HashString(data string, hashType HashType) string {
|
||||||
hasher := getHasher(hashType)
|
hasher := h.getHasher(hashType)
|
||||||
hasher.Write([]byte(data))
|
hasher.Write([]byte(data))
|
||||||
return fmt.Sprintf("%x", hasher.Sum(nil))
|
return fmt.Sprintf("%x", hasher.Sum(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
// HashBytes 计算字节数组的Hash值
|
// HashBytes 计算字节数组的Hash值
|
||||||
func HashBytes(data []byte, hashType HashType) string {
|
func (h hashUtilsStruct) HashBytes(data []byte, hashType HashType) string {
|
||||||
hasher := getHasher(hashType)
|
hasher := h.getHasher(hashType)
|
||||||
hasher.Write(data)
|
hasher.Write(data)
|
||||||
return fmt.Sprintf("%x", hasher.Sum(nil))
|
return fmt.Sprintf("%x", hasher.Sum(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
// HashFile 计算单个文件的Hash值(自动选择优化策略)
|
// HashFile 计算单个文件的Hash值(自动选择优化策略)
|
||||||
func HashFile(filename string, hashType HashType) (string, error) {
|
func (h hashUtilsStruct) HashFile(filename string, hashType HashType) (string, error) {
|
||||||
file, err := os.Open(filename)
|
file, err := os.Open(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@@ -99,20 +103,20 @@ func HashFile(filename string, hashType HashType) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
hasher := getHasher(hashType)
|
hasher := h.getHasher(hashType)
|
||||||
|
|
||||||
// 根据文件大小选择不同的处理策略
|
// 根据文件大小选择不同的处理策略
|
||||||
if fileInfo.Size() <= SmallFileThreshold {
|
if fileInfo.Size() <= SmallFileThreshold {
|
||||||
// 小文件:直接读取
|
// 小文件:直接读取
|
||||||
return hashSmallFile(file, hasher)
|
return h.hashSmallFile(file, hasher)
|
||||||
} else {
|
} else {
|
||||||
// 大文件:使用缓冲区
|
// 大文件:使用缓冲区
|
||||||
return hashLargeFile(file, hasher, fileInfo.Size())
|
return h.hashLargeFile(file, hasher, fileInfo.Size())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// hashSmallFile 处理小文件
|
// hashSmallFile 处理小文件
|
||||||
func hashSmallFile(file *os.File, hasher hash.Hash) (string, error) {
|
func (h hashUtilsStruct) hashSmallFile(file *os.File, hasher hash.Hash) (string, error) {
|
||||||
_, err := io.Copy(hasher, file)
|
_, err := io.Copy(hasher, file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@@ -121,7 +125,7 @@ func hashSmallFile(file *os.File, hasher hash.Hash) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// hashLargeFile 处理大文件
|
// hashLargeFile 处理大文件
|
||||||
func hashLargeFile(file *os.File, hasher hash.Hash, fileSize int64) (string, error) {
|
func (h hashUtilsStruct) hashLargeFile(file *os.File, hasher hash.Hash, fileSize int64) (string, error) {
|
||||||
// 根据文件大小动态调整缓冲区
|
// 根据文件大小动态调整缓冲区
|
||||||
bufferSize := DefaultBufferSize
|
bufferSize := DefaultBufferSize
|
||||||
if fileSize > 100*1024*1024 { // 大于100MB使用更大缓冲区
|
if fileSize > 100*1024*1024 { // 大于100MB使用更大缓冲区
|
||||||
@@ -147,7 +151,7 @@ func hashLargeFile(file *os.File, hasher hash.Hash, fileSize int64) (string, err
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HashFileWithProgress 计算文件Hash值并提供进度回调
|
// HashFileWithProgress 计算文件Hash值并提供进度回调
|
||||||
func HashFileWithProgress(filename string, hashType HashType, progressCallback func(processed, total int64)) (string, error) {
|
func (h hashUtilsStruct) HashFileWithProgress(filename string, hashType HashType, progressCallback func(processed, total int64)) (string, error) {
|
||||||
file, err := os.Open(filename)
|
file, err := os.Open(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@@ -159,7 +163,7 @@ func HashFileWithProgress(filename string, hashType HashType, progressCallback f
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
hasher := getHasher(hashType)
|
hasher := h.getHasher(hashType)
|
||||||
buffer := make([]byte, DefaultBufferSize)
|
buffer := make([]byte, DefaultBufferSize)
|
||||||
var processed int64
|
var processed int64
|
||||||
|
|
||||||
@@ -184,7 +188,7 @@ func HashFileWithProgress(filename string, hashType HashType, progressCallback f
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HashMultipleFiles 并发计算多个文件的Hash值
|
// HashMultipleFiles 并发计算多个文件的Hash值
|
||||||
func HashMultipleFiles(filenames []string, hashType HashType) []HashResult {
|
func (h hashUtilsStruct) HashMultipleFiles(filenames []string, hashType HashType) []HashResult {
|
||||||
// 使用CPU核心数作为并发数
|
// 使用CPU核心数作为并发数
|
||||||
numWorkers := runtime.NumCPU()
|
numWorkers := runtime.NumCPU()
|
||||||
jobs := make(chan string, len(filenames))
|
jobs := make(chan string, len(filenames))
|
||||||
@@ -197,7 +201,7 @@ func HashMultipleFiles(filenames []string, hashType HashType) []HashResult {
|
|||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
for filename := range jobs {
|
for filename := range jobs {
|
||||||
hash, err := HashFile(filename, hashType)
|
hash, err := h.HashFile(filename, hashType)
|
||||||
|
|
||||||
var size int64
|
var size int64
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -240,13 +244,13 @@ func HashMultipleFiles(filenames []string, hashType HashType) []HashResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CompareFiles 比较两个文件是否相同(通过Hash值)
|
// CompareFiles 比较两个文件是否相同(通过Hash值)
|
||||||
func CompareFiles(file1, file2 string, hashType HashType) (bool, error) {
|
func (h hashUtilsStruct) CompareFiles(file1, file2 string, hashType HashType) (bool, error) {
|
||||||
hash1, err := HashFile(file1, hashType)
|
hash1, err := h.HashFile(file1, hashType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
hash2, err := HashFile(file2, hashType)
|
hash2, err := h.HashFile(file2, hashType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@@ -255,8 +259,8 @@ func CompareFiles(file1, file2 string, hashType HashType) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// FindDuplicates 在文件列表中查找重复文件
|
// FindDuplicates 在文件列表中查找重复文件
|
||||||
func FindDuplicates(filenames []string, hashType HashType) map[string][]string {
|
func (h hashUtilsStruct) FindDuplicates(filenames []string, hashType HashType) map[string][]string {
|
||||||
results := HashMultipleFiles(filenames, hashType)
|
results := h.HashMultipleFiles(filenames, hashType)
|
||||||
duplicates := make(map[string][]string)
|
duplicates := make(map[string][]string)
|
||||||
|
|
||||||
for _, result := range results {
|
for _, result := range results {
|
||||||
@@ -276,8 +280,8 @@ func FindDuplicates(filenames []string, hashType HashType) map[string][]string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// VerifyFileIntegrity 验证文件完整性
|
// VerifyFileIntegrity 验证文件完整性
|
||||||
func VerifyFileIntegrity(filename, expectedHash string, hashType HashType) (bool, error) {
|
func (h hashUtilsStruct) VerifyFileIntegrity(filename, expectedHash string, hashType HashType) (bool, error) {
|
||||||
actualHash, err := HashFile(filename, hashType)
|
actualHash, err := h.HashFile(filename, hashType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@@ -287,7 +291,7 @@ func VerifyFileIntegrity(filename, expectedHash string, hashType HashType) (bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HashFileMultipleAlgorithms 使用多种算法计算文件Hash
|
// HashFileMultipleAlgorithms 使用多种算法计算文件Hash
|
||||||
func HashFileMultipleAlgorithms(filename string, hashTypes []HashType) (map[string]string, error) {
|
func (h hashUtilsStruct) HashFileMultipleAlgorithms(filename string, hashTypes []HashType) (map[string]string, error) {
|
||||||
file, err := os.Open(filename)
|
file, err := os.Open(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -297,7 +301,7 @@ func HashFileMultipleAlgorithms(filename string, hashTypes []HashType) (map[stri
|
|||||||
// 创建多个hasher
|
// 创建多个hasher
|
||||||
hashers := make(map[HashType]hash.Hash)
|
hashers := make(map[HashType]hash.Hash)
|
||||||
for _, hashType := range hashTypes {
|
for _, hashType := range hashTypes {
|
||||||
hashers[hashType] = getHasher(hashType)
|
hashers[hashType] = h.getHasher(hashType)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建MultiWriter
|
// 创建MultiWriter
|
||||||
@@ -316,7 +320,7 @@ func HashFileMultipleAlgorithms(filename string, hashTypes []HashType) (map[stri
|
|||||||
// 收集结果
|
// 收集结果
|
||||||
results := make(map[string]string)
|
results := make(map[string]string)
|
||||||
for hashType, hasher := range hashers {
|
for hashType, hasher := range hashers {
|
||||||
hashName := getHasherName(hashType)
|
hashName := h.getHasherName(hashType)
|
||||||
results[hashName] = fmt.Sprintf("%x", hasher.Sum(nil))
|
results[hashName] = fmt.Sprintf("%x", hasher.Sum(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,26 +328,26 @@ func HashFileMultipleAlgorithms(filename string, hashTypes []HashType) (map[stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 便捷函数
|
// 便捷函数
|
||||||
func MD5File(filename string) (string, error) {
|
func (h hashUtilsStruct) MD5File(filename string) (string, error) {
|
||||||
return HashFile(filename, MD5)
|
return h.HashFile(filename, MD5)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SHA1File(filename string) (string, error) {
|
func (h hashUtilsStruct) SHA1File(filename string) (string, error) {
|
||||||
return HashFile(filename, SHA1)
|
return h.HashFile(filename, SHA1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SHA256File(filename string) (string, error) {
|
func (h hashUtilsStruct) SHA256File(filename string) (string, error) {
|
||||||
return HashFile(filename, SHA256)
|
return h.HashFile(filename, SHA256)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SHA512File(filename string) (string, error) {
|
func (h hashUtilsStruct) SHA512File(filename string) (string, error) {
|
||||||
return HashFile(filename, SHA512)
|
return h.HashFile(filename, SHA512)
|
||||||
}
|
}
|
||||||
|
|
||||||
func MD5String(data string) string {
|
func (h hashUtilsStruct) MD5String(data string) string {
|
||||||
return HashString(data, MD5)
|
return h.HashString(data, MD5)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SHA256String(data string) string {
|
func (h hashUtilsStruct) SHA256String(data string) string {
|
||||||
return HashString(data, SHA256)
|
return h.HashString(data, SHA256)
|
||||||
}
|
}
|
||||||
@@ -53,7 +53,7 @@ func TestMain(m *testing.M) {
|
|||||||
fmt.Println("Testing SHA-256...")
|
fmt.Println("Testing SHA-256...")
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
start256 := time.Now()
|
start256 := time.Now()
|
||||||
sha256Time, err := SHA256File(fileName)
|
sha256Time, err := HashUtils.SHA256File(fileName)
|
||||||
elapsed256 := time.Since(start256)
|
elapsed256 := time.Since(start256)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -66,7 +66,7 @@ func TestMain(m *testing.M) {
|
|||||||
fmt.Println("Testing SHA-512...")
|
fmt.Println("Testing SHA-512...")
|
||||||
for i := 0; i < 5; i++ {
|
for i := 0; i < 5; i++ {
|
||||||
start512 := time.Now()
|
start512 := time.Now()
|
||||||
sha512Time, err := SHA512File(fileName)
|
sha512Time, err := HashUtils.SHA512File(fileName)
|
||||||
elapsed512 := time.Since(start512)
|
elapsed512 := time.Since(start512)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("SHA-512 error:", err)
|
fmt.Println("SHA-512 error:", err)
|
||||||
|
|||||||
Reference in new issue
Block a user