Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
eada7e8911 | |||
3bc6282168 | |||
7a9cd55fb2 | |||
0185a00836 | |||
4d7668c9ff | |||
e65d101476 | |||
0ce7678c21 | |||
c08b4bf90d | |||
74e88e62d5 | |||
55275db618 | |||
a72c860511 | |||
2ba0097a06 | |||
ad8514cf0f | |||
2415f59f7f | |||
a3a57b9380 | |||
03d8049803 | |||
55488a023e |
22
go.mod
22
go.mod
@ -1,4 +1,4 @@
|
||||
module git.bvbej.com/bvbej/base-golang
|
||||
module gitea.bvbej.com/bvbej/base-golang
|
||||
|
||||
go 1.22.5
|
||||
|
||||
@ -17,26 +17,26 @@ require (
|
||||
github.com/panjf2000/ants/v2 v2.10.0
|
||||
github.com/prometheus/client_golang v1.19.1
|
||||
github.com/qiniu/go-sdk/v7 v7.21.1
|
||||
github.com/redis/go-redis/v9 v9.6.0
|
||||
github.com/redis/go-redis/v9 v9.6.1
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/rs/cors v1.11.0
|
||||
github.com/rs/cors/wrapper/gin v0.0.0-20240515105523-1562b1715b35
|
||||
github.com/speps/go-hashids v2.0.0+incompatible
|
||||
github.com/spf13/cast v1.6.0
|
||||
github.com/spf13/cast v1.7.0
|
||||
github.com/spf13/viper v1.19.0
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/tidwall/buntdb v1.3.1
|
||||
github.com/tidwall/gjson v1.17.1
|
||||
github.com/tidwall/gjson v1.17.3
|
||||
github.com/tus/tusd v1.13.0
|
||||
github.com/xuri/excelize/v2 v2.8.1
|
||||
go.mongodb.org/mongo-driver v1.16.0
|
||||
go.mongodb.org/mongo-driver v1.16.1
|
||||
go.uber.org/atomic v1.11.0
|
||||
go.uber.org/multierr v1.11.0
|
||||
go.uber.org/zap v1.27.0
|
||||
golang.org/x/crypto v0.25.0
|
||||
golang.org/x/net v0.27.0
|
||||
golang.org/x/sync v0.7.0
|
||||
golang.org/x/time v0.5.0
|
||||
golang.org/x/crypto v0.26.0
|
||||
golang.org/x/net v0.28.0
|
||||
golang.org/x/sync v0.8.0
|
||||
golang.org/x/time v0.6.0
|
||||
google.golang.org/grpc v1.65.0
|
||||
google.golang.org/protobuf v1.34.2
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
@ -110,8 +110,8 @@ require (
|
||||
golang.org/x/arch v0.8.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect
|
||||
golang.org/x/image v0.14.0 // indirect
|
||||
golang.org/x/sys v0.22.0 // indirect
|
||||
golang.org/x/text v0.16.0 // indirect
|
||||
golang.org/x/sys v0.23.0 // indirect
|
||||
golang.org/x/text v0.17.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
|
40
go.sum
40
go.sum
@ -1246,8 +1246,8 @@ github.com/qiniu/dyn v1.3.0/go.mod h1:E8oERcm8TtwJiZvkQPbcAh0RL8jO1G0VXJMW3FAWdk
|
||||
github.com/qiniu/go-sdk/v7 v7.21.1 h1:D/IjVOlg5pTw0jeDjqTo6H5QM73Obb1AYfPOHmIFN+Q=
|
||||
github.com/qiniu/go-sdk/v7 v7.21.1/go.mod h1:8EM2awITynlem2VML2dXGHkMYP2UyECsGLOdp6yMpco=
|
||||
github.com/qiniu/x v1.10.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs=
|
||||
github.com/redis/go-redis/v9 v9.6.0 h1:NLck+Rab3AOTHw21CGRpvQpgTrAU4sgdCswqGtlhGRA=
|
||||
github.com/redis/go-redis/v9 v9.6.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
|
||||
github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4=
|
||||
github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
|
||||
@ -1293,8 +1293,8 @@ github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcD
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
||||
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
@ -1332,8 +1332,8 @@ github.com/tidwall/btree v1.4.2/go.mod h1:LGm8L/DZjPLmeWGjv5kFrY8dL4uVhMmzmmLYms
|
||||
github.com/tidwall/buntdb v1.3.1 h1:HKoDF01/aBhl9RjYtbaLnvX9/OuenwvQiC3OP1CcL4o=
|
||||
github.com/tidwall/buntdb v1.3.1/go.mod h1:lZZrZUWzlyDJKlLQ6DKAy53LnG7m5kHyrEHvvcDmBpU=
|
||||
github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U=
|
||||
github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.17.3 h1:bwWLZU7icoKRG+C+0PNwIKC6FCJO/Q3p2pZvuP0jN94=
|
||||
github.com/tidwall/gjson v1.17.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/grect v0.1.4 h1:dA3oIgNgWdSspFzn1kS4S/RDpZFLrIxAZOdJKjYapOg=
|
||||
github.com/tidwall/grect v0.1.4/go.mod h1:9FBsaYRaR0Tcy4UwefBX/UDcDcDy9V5jUcxHzv2jd5Q=
|
||||
github.com/tidwall/lotsa v1.0.2 h1:dNVBH5MErdaQ/xd9s769R31/n2dXavsQ0Yf4TMEHHw8=
|
||||
@ -1380,8 +1380,8 @@ github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaD
|
||||
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
||||
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
|
||||
go.mongodb.org/mongo-driver v1.16.0 h1:tpRsfBJMROVHKpdGyc1BBEzzjDUWjItxbVSZ8Ls4BQ4=
|
||||
go.mongodb.org/mongo-driver v1.16.0/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw=
|
||||
go.mongodb.org/mongo-driver v1.16.1 h1:rIVLL3q0IHM39dvE+z2ulZLp9ENZKThVfuvN/IiN4l8=
|
||||
go.mongodb.org/mongo-driver v1.16.1/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
@ -1429,8 +1429,8 @@ golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0
|
||||
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
|
||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@ -1562,8 +1562,8 @@ golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@ -1615,8 +1615,8 @@ golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -1716,8 +1716,8 @@ golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
|
||||
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
@ -1751,16 +1751,16 @@ golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
||||
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
@ -4,7 +4,7 @@ import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/android_binary"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/android_binary"
|
||||
"image"
|
||||
"io"
|
||||
"os"
|
||||
|
@ -1,6 +1,6 @@
|
||||
package apk
|
||||
|
||||
import "git.bvbej.com/bvbej/base-golang/pkg/android_binary"
|
||||
import "gitea.bvbej.com/bvbej/base-golang/pkg/android_binary"
|
||||
|
||||
// Instrumentation is an application instrumentation code.
|
||||
type Instrumentation struct {
|
||||
|
@ -3,7 +3,7 @@ package ants
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/ticker"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/ticker"
|
||||
"github.com/panjf2000/ants/v2"
|
||||
"go.uber.org/zap"
|
||||
"runtime/debug"
|
||||
|
@ -3,7 +3,7 @@ package apollo
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/env"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/env"
|
||||
"github.com/apolloconfig/agollo/v4"
|
||||
"github.com/apolloconfig/agollo/v4/component/log"
|
||||
apolloConfig "github.com/apolloconfig/agollo/v4/env/config"
|
||||
|
4
pkg/cache/redis.go
vendored
4
pkg/cache/redis.go
vendored
@ -6,8 +6,8 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/time_parse"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/trace"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/time_parse"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/trace"
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
|
@ -2,7 +2,7 @@ package captcha
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/cache"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/cache"
|
||||
"github.com/mojocn/base64Captcha"
|
||||
"go.uber.org/zap"
|
||||
"strings"
|
||||
|
@ -7,8 +7,8 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/time_parse"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/trace"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/time_parse"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/trace"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
|
@ -1,6 +1,6 @@
|
||||
package database
|
||||
|
||||
import "git.bvbej.com/bvbej/base-golang/pkg/trace"
|
||||
import "gitea.bvbej.com/bvbej/base-golang/pkg/trace"
|
||||
|
||||
type Trace = trace.T
|
||||
|
||||
|
23
pkg/downloader/base/constants.go
Normal file
23
pkg/downloader/base/constants.go
Normal file
@ -0,0 +1,23 @@
|
||||
package base
|
||||
|
||||
type Status int
|
||||
|
||||
const (
|
||||
DownloadStatusReady Status = iota
|
||||
DownloadStatusStart
|
||||
DownloadStatusPause
|
||||
DownloadStatusError
|
||||
DownloadStatusDone
|
||||
)
|
||||
|
||||
const (
|
||||
HttpCodeOK = 200
|
||||
HttpCodePartialContent = 206
|
||||
|
||||
HttpHeaderRange = "Range"
|
||||
HttpHeaderContentLength = "Content-Length"
|
||||
HttpHeaderContentRange = "Content-Range"
|
||||
HttpHeaderContentDisposition = "Content-Disposition"
|
||||
|
||||
HttpHeaderRangeFormat = "bytes=%d-%d"
|
||||
)
|
37
pkg/downloader/base/model.go
Normal file
37
pkg/downloader/base/model.go
Normal file
@ -0,0 +1,37 @@
|
||||
package base
|
||||
|
||||
// Request 下载请求
|
||||
type Request struct {
|
||||
// 下载链接
|
||||
URL string
|
||||
// 附加信息
|
||||
Extra any
|
||||
}
|
||||
|
||||
// Resource 资源信息
|
||||
type Resource struct {
|
||||
Req *Request
|
||||
// 资源总大小
|
||||
TotalSize int64
|
||||
// 是否支持断点下载
|
||||
Range bool
|
||||
// 资源所包含的文件列表
|
||||
Files []*FileInfo
|
||||
}
|
||||
|
||||
// FileInfo 文件信息
|
||||
type FileInfo struct {
|
||||
Name string
|
||||
Path string
|
||||
Size int64
|
||||
}
|
||||
|
||||
// Options 下载选项
|
||||
type Options struct {
|
||||
// 保存文件名
|
||||
Name string
|
||||
// 保存目录
|
||||
Path string
|
||||
// 并发连接数
|
||||
Connections int
|
||||
}
|
172
pkg/downloader/controller/controller.go
Normal file
172
pkg/downloader/controller/controller.go
Normal file
@ -0,0 +1,172 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"golang.org/x/net/proxy"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Controller interface {
|
||||
Touch(name string, size int64) (file *os.File, err error)
|
||||
Open(name string) (file *os.File, err error)
|
||||
Write(name string, offset int64, buf []byte) (int, error)
|
||||
Close(name string) error
|
||||
ContextDialer() (proxy.Dialer, error)
|
||||
ContextCookie() http.CookieJar
|
||||
ContextTimeout() time.Duration
|
||||
ContextProxy() func(*http.Request) (*url.URL, error)
|
||||
}
|
||||
|
||||
type Option func(*option)
|
||||
|
||||
type option struct {
|
||||
CookieJar http.CookieJar
|
||||
Timeout time.Duration
|
||||
Dialer proxy.Dialer
|
||||
Proxy func(*http.Request) (*url.URL, error)
|
||||
}
|
||||
|
||||
func WithCookie(cookieJar http.CookieJar) Option {
|
||||
return func(opt *option) {
|
||||
opt.CookieJar = cookieJar
|
||||
}
|
||||
}
|
||||
|
||||
func WithTimeout(timeout time.Duration) Option {
|
||||
return func(opt *option) {
|
||||
opt.Timeout = timeout
|
||||
}
|
||||
}
|
||||
|
||||
func WithDialer(dialer proxy.Dialer) Option {
|
||||
return func(opt *option) {
|
||||
opt.Dialer = dialer
|
||||
}
|
||||
}
|
||||
|
||||
func WithProxy(fn func(*http.Request) (*url.URL, error)) Option {
|
||||
return func(opt *option) {
|
||||
opt.Proxy = fn
|
||||
}
|
||||
}
|
||||
|
||||
type DefaultController struct {
|
||||
*option
|
||||
Files map[string]*os.File
|
||||
}
|
||||
|
||||
func NewController(options ...Option) *DefaultController {
|
||||
opt := new(option)
|
||||
for _, f := range options {
|
||||
f(opt)
|
||||
}
|
||||
if opt.Timeout == 0 {
|
||||
opt.Timeout = time.Second * 30
|
||||
}
|
||||
if opt.Dialer == nil {
|
||||
opt.Dialer = proxy.FromEnvironment()
|
||||
}
|
||||
return &DefaultController{
|
||||
Files: make(map[string]*os.File),
|
||||
option: opt,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *DefaultController) Touch(name string, size int64) (file *os.File, err error) {
|
||||
file, err = os.Create(name)
|
||||
if size > 0 {
|
||||
err = os.Truncate(name, size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err == nil {
|
||||
c.Files[name] = file
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *DefaultController) Open(name string) (file *os.File, err error) {
|
||||
file, err = os.OpenFile(name, os.O_RDWR, os.ModePerm)
|
||||
if err == nil {
|
||||
c.Files[name] = file
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *DefaultController) Write(name string, offset int64, buf []byte) (int, error) {
|
||||
return c.Files[name].WriteAt(buf, offset)
|
||||
}
|
||||
|
||||
func (c *DefaultController) Close(name string) error {
|
||||
err := c.Files[name].Close()
|
||||
delete(c.Files, name)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *DefaultController) ContextDialer() (proxy.Dialer, error) {
|
||||
return &DialerWarp{dialer: c.Dialer}, nil
|
||||
}
|
||||
|
||||
func (c *DefaultController) ContextCookie() http.CookieJar {
|
||||
return c.CookieJar
|
||||
}
|
||||
|
||||
func (c *DefaultController) ContextTimeout() time.Duration {
|
||||
return c.Timeout
|
||||
}
|
||||
|
||||
func (c *DefaultController) ContextProxy() func(*http.Request) (*url.URL, error) {
|
||||
return c.Proxy
|
||||
}
|
||||
|
||||
type DialerWarp struct {
|
||||
dialer proxy.Dialer
|
||||
}
|
||||
|
||||
type ConnWarp struct {
|
||||
conn net.Conn
|
||||
}
|
||||
|
||||
func (c *ConnWarp) Read(b []byte) (n int, err error) {
|
||||
return c.conn.Read(b)
|
||||
}
|
||||
|
||||
func (c *ConnWarp) Write(b []byte) (n int, err error) {
|
||||
return c.conn.Write(b)
|
||||
}
|
||||
|
||||
func (c *ConnWarp) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func (c *ConnWarp) LocalAddr() net.Addr {
|
||||
return c.conn.LocalAddr()
|
||||
}
|
||||
|
||||
func (c *ConnWarp) RemoteAddr() net.Addr {
|
||||
return c.conn.RemoteAddr()
|
||||
}
|
||||
|
||||
func (c *ConnWarp) SetDeadline(t time.Time) error {
|
||||
return c.conn.SetDeadline(t)
|
||||
}
|
||||
|
||||
func (c *ConnWarp) SetReadDeadline(t time.Time) error {
|
||||
return c.conn.SetReadDeadline(t)
|
||||
}
|
||||
|
||||
func (c *ConnWarp) SetWriteDeadline(t time.Time) error {
|
||||
return c.conn.SetWriteDeadline(t)
|
||||
}
|
||||
|
||||
func (d *DialerWarp) Dial(network, addr string) (c net.Conn, err error) {
|
||||
conn, err := d.dialer.Dial(network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ConnWarp{conn: conn}, nil
|
||||
}
|
@ -2,11 +2,11 @@ package downloader
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/downloader/base"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/downloader/controller"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/downloader/fetcher"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/downloader/protocol/http"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/downloader/util"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/downloader/base"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/downloader/controller"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/downloader/fetcher"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/downloader/protocol/http"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/downloader/util"
|
||||
"github.com/google/uuid"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
@ -2,9 +2,9 @@ package downloader
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/downloader/base"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/downloader/controller"
|
||||
"git.bvbej.com/bvbej/base-golang/tool"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/downloader/base"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/downloader/controller"
|
||||
"gitea.bvbej.com/bvbej/base-golang/tool"
|
||||
"golang.org/x/net/proxy"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
52
pkg/downloader/fetcher/fetcher.go
Normal file
52
pkg/downloader/fetcher/fetcher.go
Normal file
@ -0,0 +1,52 @@
|
||||
package fetcher
|
||||
|
||||
import (
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/downloader/base"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/downloader/controller"
|
||||
)
|
||||
|
||||
// Fetcher 对应协议的下载支持
|
||||
type Fetcher interface {
|
||||
// Setup 设置文件相关信息
|
||||
Setup(ctl controller.Controller)
|
||||
// Resolve 解析请求
|
||||
Resolve(req *base.Request) (res *base.Resource, err error)
|
||||
// Create 创建任务
|
||||
Create(res *base.Resource, opts *base.Options) (err error)
|
||||
// Start 开始
|
||||
Start() (err error)
|
||||
// Pause 暂停
|
||||
Pause() (err error)
|
||||
// Continue 继续
|
||||
Continue() (err error)
|
||||
// Progress 获取任务各个文件下载进度
|
||||
Progress() Progress
|
||||
// Wait 该方法会一直阻塞,直到任务下载结束
|
||||
Wait() (err error)
|
||||
}
|
||||
|
||||
type DefaultFetcher struct {
|
||||
Ctl controller.Controller
|
||||
DoneCh chan error
|
||||
}
|
||||
|
||||
func (f *DefaultFetcher) Setup(ctl controller.Controller) {
|
||||
f.Ctl = ctl
|
||||
f.DoneCh = make(chan error, 1)
|
||||
}
|
||||
|
||||
func (f *DefaultFetcher) Wait() (err error) {
|
||||
return <-f.DoneCh
|
||||
}
|
||||
|
||||
// Progress 获取任务中各个文件的已下载字节数
|
||||
type Progress []int64
|
||||
|
||||
// TotalDownloaded 获取任务总下载字节数
|
||||
func (p Progress) TotalDownloaded() int64 {
|
||||
total := int64(0)
|
||||
for _, d := range p {
|
||||
total += d
|
||||
}
|
||||
return total
|
||||
}
|
409
pkg/downloader/protocol/http/fetcher.go
Normal file
409
pkg/downloader/protocol/http/fetcher.go
Normal file
@ -0,0 +1,409 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/downloader/base"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/downloader/fetcher"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"io"
|
||||
"mime"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type RequestError struct {
|
||||
Code int
|
||||
Msg string
|
||||
}
|
||||
|
||||
func NewRequestError(code int, msg string) *RequestError {
|
||||
return &RequestError{Code: code, Msg: msg}
|
||||
}
|
||||
|
||||
func (re *RequestError) Error() string {
|
||||
return fmt.Sprintf("http request fail,code:%d", re.Code)
|
||||
}
|
||||
|
||||
type Fetcher struct {
|
||||
*fetcher.DefaultFetcher
|
||||
|
||||
res *base.Resource
|
||||
opts *base.Options
|
||||
status base.Status
|
||||
clients []*http.Response
|
||||
chunks []*Chunk
|
||||
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
pauseCh chan any
|
||||
}
|
||||
|
||||
func NewFetcher() *Fetcher {
|
||||
return &Fetcher{
|
||||
DefaultFetcher: new(fetcher.DefaultFetcher),
|
||||
pauseCh: make(chan any),
|
||||
}
|
||||
}
|
||||
|
||||
var protocols = []string{"HTTP", "HTTPS"}
|
||||
|
||||
func FetcherBuilder() ([]string, func() fetcher.Fetcher) {
|
||||
return protocols, func() fetcher.Fetcher {
|
||||
return NewFetcher()
|
||||
}
|
||||
}
|
||||
|
||||
func (f *Fetcher) Resolve(req *base.Request) (*base.Resource, error) {
|
||||
httpReq, err := f.buildRequest(nil, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client, err := f.buildClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 只访问一个字节,测试资源是否支持Range请求
|
||||
httpReq.Header.Set(base.HttpHeaderRange, fmt.Sprintf(base.HttpHeaderRangeFormat, 0, 0))
|
||||
httpResp, err := client.Do(httpReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 拿到响应头就关闭,不用加defer
|
||||
_ = httpResp.Body.Close()
|
||||
res := &base.Resource{
|
||||
Req: req,
|
||||
Range: false,
|
||||
Files: []*base.FileInfo{},
|
||||
}
|
||||
if base.HttpCodePartialContent == httpResp.StatusCode {
|
||||
// 返回206响应码表示支持断点下载
|
||||
res.Range = true
|
||||
// 解析资源大小: bytes 0-1000/1001 => 1001
|
||||
contentTotal := path.Base(httpResp.Header.Get(base.HttpHeaderContentRange))
|
||||
if contentTotal != "" {
|
||||
parse, err := strconv.ParseInt(contentTotal, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.TotalSize = parse
|
||||
}
|
||||
} else if base.HttpCodeOK == httpResp.StatusCode {
|
||||
// 返回200响应码,不支持断点下载,通过Content-Length头获取文件大小,获取不到的话可能是chunked编码
|
||||
contentLength := httpResp.Header.Get(base.HttpHeaderContentLength)
|
||||
if contentLength != "" {
|
||||
parse, err := strconv.ParseInt(contentLength, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.TotalSize = parse
|
||||
}
|
||||
} else {
|
||||
return nil, NewRequestError(httpResp.StatusCode, httpResp.Status)
|
||||
}
|
||||
file := &base.FileInfo{
|
||||
Size: res.TotalSize,
|
||||
}
|
||||
contentDisposition := httpResp.Header.Get(base.HttpHeaderContentDisposition)
|
||||
if contentDisposition != "" {
|
||||
_, params, _ := mime.ParseMediaType(contentDisposition)
|
||||
filename := params["filename"]
|
||||
if filename != "" {
|
||||
file.Name = filename
|
||||
}
|
||||
}
|
||||
// Get file filename by URL
|
||||
if file.Name == "" && strings.Count(req.URL, "/") > 2 {
|
||||
file.Name = filepath.Base(req.URL)
|
||||
}
|
||||
// unknown file filename
|
||||
if file.Name == "" {
|
||||
file.Name = "unknown"
|
||||
}
|
||||
res.Files = append(res.Files, file)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (f *Fetcher) Create(res *base.Resource, opts *base.Options) error {
|
||||
f.res = res
|
||||
f.opts = opts
|
||||
f.status = base.DownloadStatusReady
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *Fetcher) Start() (err error) {
|
||||
// 创建文件
|
||||
name := f.filename()
|
||||
_, err = f.Ctl.Touch(name, f.res.TotalSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.status = base.DownloadStatusStart
|
||||
if f.res.Range {
|
||||
// 每个连接平均需要下载的分块大小
|
||||
chunkSize := f.res.TotalSize / int64(f.opts.Connections)
|
||||
f.chunks = make([]*Chunk, f.opts.Connections)
|
||||
f.clients = make([]*http.Response, f.opts.Connections)
|
||||
for i := 0; i < f.opts.Connections; i++ {
|
||||
var (
|
||||
begin = chunkSize * int64(i)
|
||||
end int64
|
||||
)
|
||||
if i == f.opts.Connections-1 {
|
||||
// 最后一个分块需要保证把文件下载完
|
||||
end = f.res.TotalSize - 1
|
||||
} else {
|
||||
end = begin + chunkSize - 1
|
||||
}
|
||||
chunk := NewChunk(begin, end)
|
||||
f.chunks[i] = chunk
|
||||
}
|
||||
} else {
|
||||
// 只支持单连接下载
|
||||
f.chunks = make([]*Chunk, 1)
|
||||
f.clients = make([]*http.Response, 1)
|
||||
f.chunks[0] = NewChunk(0, 0)
|
||||
}
|
||||
f.fetch()
|
||||
return
|
||||
}
|
||||
|
||||
func (f *Fetcher) Pause() (err error) {
|
||||
if base.DownloadStatusStart != f.status {
|
||||
return
|
||||
}
|
||||
f.status = base.DownloadStatusPause
|
||||
f.cancel()
|
||||
<-f.pauseCh
|
||||
return
|
||||
}
|
||||
|
||||
func (f *Fetcher) Continue() (err error) {
|
||||
if base.DownloadStatusStart == f.status || base.DownloadStatusDone == f.status {
|
||||
return
|
||||
}
|
||||
f.status = base.DownloadStatusStart
|
||||
var name = f.filename()
|
||||
_, err = f.Ctl.Open(name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.fetch()
|
||||
return
|
||||
}
|
||||
|
||||
func (f *Fetcher) Progress() fetcher.Progress {
|
||||
p := make(fetcher.Progress, 0)
|
||||
if len(f.chunks) > 0 {
|
||||
total := int64(0)
|
||||
for _, chunk := range f.chunks {
|
||||
total += chunk.Downloaded
|
||||
}
|
||||
p = append(p, total)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func (f *Fetcher) filename() string {
|
||||
// 创建文件
|
||||
var filename = f.opts.Name
|
||||
if filename == "" {
|
||||
filename = f.res.Files[0].Name
|
||||
}
|
||||
return filepath.Join(f.opts.Path, filename)
|
||||
}
|
||||
|
||||
func (f *Fetcher) fetch() {
|
||||
f.ctx, f.cancel = context.WithCancel(context.Background())
|
||||
eg, _ := errgroup.WithContext(f.ctx)
|
||||
for i := 0; i < f.opts.Connections; i++ {
|
||||
eg.Go(func() error {
|
||||
return f.fetchChunk(i)
|
||||
})
|
||||
}
|
||||
|
||||
go func() {
|
||||
err := eg.Wait()
|
||||
// 下载停止,关闭文件句柄
|
||||
_ = f.Ctl.Close(f.filename())
|
||||
if f.status == base.DownloadStatusPause {
|
||||
f.pauseCh <- nil
|
||||
} else {
|
||||
if err != nil {
|
||||
f.status = base.DownloadStatusError
|
||||
} else {
|
||||
f.status = base.DownloadStatusDone
|
||||
}
|
||||
f.DoneCh <- err
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (f *Fetcher) fetchChunk(index int) (err error) {
|
||||
filename := f.filename()
|
||||
chunk := f.chunks[index]
|
||||
|
||||
httpReq, err := f.buildRequest(f.ctx, f.res.Req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
client, err := f.buildClient()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var buf = make([]byte, 64*1024) // 64KB
|
||||
|
||||
// 重试10次
|
||||
for i := 0; i < 10; i++ {
|
||||
// 如果下载完成直接返回
|
||||
if chunk.Status == base.DownloadStatusDone {
|
||||
return
|
||||
}
|
||||
// 如果已暂停直接跳出
|
||||
if f.status == base.DownloadStatusPause {
|
||||
break
|
||||
}
|
||||
var (
|
||||
resp *http.Response
|
||||
retry bool
|
||||
)
|
||||
if f.res.Range {
|
||||
httpReq.Header.Set(base.HttpHeaderRange,
|
||||
fmt.Sprintf(base.HttpHeaderRangeFormat, chunk.Begin+chunk.Downloaded, chunk.End))
|
||||
} else {
|
||||
chunk.Downloaded = 0
|
||||
}
|
||||
err = func() error {
|
||||
resp, err = client.Do(httpReq)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.clients[index] = resp
|
||||
if resp.StatusCode != base.HttpCodeOK && resp.StatusCode != base.HttpCodePartialContent {
|
||||
err = NewRequestError(resp.StatusCode, resp.Status)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}()
|
||||
if err != nil {
|
||||
//请求失败3s后重试
|
||||
time.Sleep(time.Second * 3)
|
||||
continue
|
||||
}
|
||||
|
||||
// 请求成功就重置错误次数,连续失败10次才终止
|
||||
i = 0
|
||||
|
||||
retry, err = func() (bool, error) {
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
var n int
|
||||
for {
|
||||
n, err = resp.Body.Read(buf)
|
||||
if n > 0 {
|
||||
_, err = f.Ctl.Write(filename, chunk.Begin+chunk.Downloaded, buf[:n])
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
chunk.Downloaded += int64(n)
|
||||
}
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
return true, err
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}()
|
||||
if !retry {
|
||||
// 下载成功,跳出重试
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if f.status == base.DownloadStatusPause {
|
||||
chunk.Status = base.DownloadStatusPause
|
||||
} else if chunk.Downloaded >= chunk.End-chunk.Begin+1 {
|
||||
chunk.Status = base.DownloadStatusDone
|
||||
} else {
|
||||
if err != nil {
|
||||
chunk.Status = base.DownloadStatusError
|
||||
} else {
|
||||
chunk.Status = base.DownloadStatusDone
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (f *Fetcher) buildClient() (*http.Client, error) {
|
||||
dialer, err := f.Ctl.ContextDialer()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
transport := &http.Transport{
|
||||
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
return dialer.Dial(network, addr)
|
||||
},
|
||||
}
|
||||
if f.Ctl.ContextProxy() != nil {
|
||||
transport.Proxy = f.Ctl.ContextProxy()
|
||||
}
|
||||
return &http.Client{
|
||||
Jar: f.Ctl.ContextCookie(),
|
||||
Timeout: f.Ctl.ContextTimeout(),
|
||||
Transport: transport,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *Fetcher) buildRequest(ctx context.Context, req *base.Request) (httpReq *http.Request, err error) {
|
||||
reqUrl, err := url.Parse(req.URL)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
method string
|
||||
body io.Reader
|
||||
)
|
||||
headers := make(map[string][]string)
|
||||
if req.Extra == nil {
|
||||
method = http.MethodGet
|
||||
} else {
|
||||
extra := req.Extra.(Extra)
|
||||
if extra.Method != "" {
|
||||
method = extra.Method
|
||||
} else {
|
||||
method = http.MethodGet
|
||||
}
|
||||
if len(extra.Header) > 0 {
|
||||
for k, v := range extra.Header {
|
||||
headers[k] = []string{v}
|
||||
}
|
||||
}
|
||||
if extra.Body != "" {
|
||||
body = io.NopCloser(bytes.NewBufferString(extra.Body))
|
||||
}
|
||||
}
|
||||
|
||||
if ctx != nil {
|
||||
httpReq, err = http.NewRequestWithContext(ctx, method, reqUrl.String(), body)
|
||||
} else {
|
||||
httpReq, err = http.NewRequest(method, reqUrl.String(), body)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
httpReq.Header = headers
|
||||
return httpReq, nil
|
||||
}
|
24
pkg/downloader/protocol/http/model.go
Normal file
24
pkg/downloader/protocol/http/model.go
Normal file
@ -0,0 +1,24 @@
|
||||
package http
|
||||
|
||||
import "gitea.bvbej.com/bvbej/base-golang/pkg/downloader/base"
|
||||
|
||||
type Chunk struct {
|
||||
Status base.Status
|
||||
Begin int64
|
||||
End int64
|
||||
Downloaded int64
|
||||
}
|
||||
|
||||
func NewChunk(begin int64, end int64) *Chunk {
|
||||
return &Chunk{
|
||||
Status: base.DownloadStatusReady,
|
||||
Begin: begin,
|
||||
End: end,
|
||||
}
|
||||
}
|
||||
|
||||
type Extra struct {
|
||||
Method string
|
||||
Header map[string]string
|
||||
Body string
|
||||
}
|
25
pkg/downloader/util/timer.go
Normal file
25
pkg/downloader/util/timer.go
Normal file
@ -0,0 +1,25 @@
|
||||
package util
|
||||
|
||||
import "time"
|
||||
|
||||
// Timer 计时器
|
||||
type Timer struct {
|
||||
t int64
|
||||
used int64
|
||||
}
|
||||
|
||||
func (t *Timer) Start() {
|
||||
t.t = time.Now().UnixNano()
|
||||
}
|
||||
|
||||
func (t *Timer) Pause() {
|
||||
t.used += time.Now().UnixNano() - t.t
|
||||
}
|
||||
|
||||
func (t *Timer) Continue() {
|
||||
t.t = time.Now().UnixNano()
|
||||
}
|
||||
|
||||
func (t *Timer) Used() int64 {
|
||||
return (time.Now().UnixNano() - t.t) + t.used
|
||||
}
|
@ -10,7 +10,7 @@ import (
|
||||
httpURL "net/url"
|
||||
"time"
|
||||
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/trace"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/trace"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -4,7 +4,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/trace"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/trace"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/trace"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/trace"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
package limiter
|
||||
|
||||
import (
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/ticker"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/ticker"
|
||||
"golang.org/x/time/rate"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -9,8 +9,8 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/errno"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/trace"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/errno"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/trace"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
|
@ -8,12 +8,12 @@ import (
|
||||
"runtime/debug"
|
||||
"time"
|
||||
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/color"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/env"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/errno"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/limiter"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/trace"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/validator"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/color"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/env"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/errno"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/limiter"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/trace"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/validator"
|
||||
"github.com/gin-contrib/pprof"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
|
@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/trace"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/trace"
|
||||
)
|
||||
|
||||
type Option func(*option)
|
||||
|
@ -4,8 +4,8 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/md5"
|
||||
"git.bvbej.com/bvbej/base-golang/tool"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/md5"
|
||||
"gitea.bvbej.com/bvbej/base-golang/tool"
|
||||
"github.com/qiniu/go-sdk/v7/auth/qbox"
|
||||
"github.com/qiniu/go-sdk/v7/storage"
|
||||
"github.com/tidwall/gjson"
|
||||
@ -57,12 +57,10 @@ type PutRet struct {
|
||||
}
|
||||
|
||||
func New(accessKey, secretKey, bucket, domain, securityKey string) QiNiu {
|
||||
region, _ := storage.GetRegion(accessKey, bucket)
|
||||
mac := qbox.NewMac(accessKey, secretKey)
|
||||
conf := &storage.Config{
|
||||
Region: region, //空间所在的存储区域
|
||||
UseHTTPS: true, //是否使用https域名
|
||||
UseCdnDomains: true, //上传是否使用CDN上传加速
|
||||
UseCdnDomains: false, //上传是否使用CDN上传加速
|
||||
}
|
||||
return &qiNiu{
|
||||
mac: mac,
|
||||
@ -216,7 +214,7 @@ func (q *qiNiu) GetFileHash(path, qhash string) (hash string, err error) {
|
||||
}
|
||||
|
||||
sign := q.TimestampSecuritySign(path, time.Second*5)
|
||||
addr := fmt.Sprintf("https://cdn.mogume.com/%s?%s&qhash/%s", path, sign, qhash)
|
||||
addr := fmt.Sprintf("%s/%s?%s&qhash/%s", strings.TrimRight(q.domain, "/"), path, sign, qhash)
|
||||
|
||||
resp, err := http.Get(addr)
|
||||
if err != nil {
|
||||
|
@ -10,7 +10,7 @@ import (
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/time_parse"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/time_parse"
|
||||
)
|
||||
|
||||
// Generate
|
||||
|
@ -11,7 +11,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/time_parse"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/time_parse"
|
||||
)
|
||||
|
||||
func (s *signature) Verify(authorization, date string, path string, method string, params url.Values) (ok bool, err error) {
|
||||
|
@ -5,9 +5,9 @@ import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/color"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/ticker"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/token"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/color"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/ticker"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/token"
|
||||
"github.com/rs/cors"
|
||||
"github.com/tus/tusd/pkg/filestore"
|
||||
tus "github.com/tus/tusd/pkg/handler"
|
||||
|
@ -3,12 +3,12 @@ package client
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/ticker"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/websocket/client/service"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/websocket/codec"
|
||||
_ "git.bvbej.com/bvbej/base-golang/pkg/websocket/codec/json"
|
||||
_ "git.bvbej.com/bvbej/base-golang/pkg/websocket/codec/protobuf"
|
||||
"git.bvbej.com/bvbej/base-golang/tool"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/ticker"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/websocket/client/service"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/websocket/codec"
|
||||
_ "gitea.bvbej.com/bvbej/base-golang/pkg/websocket/codec/json"
|
||||
_ "gitea.bvbej.com/bvbej/base-golang/pkg/websocket/codec/protobuf"
|
||||
"gitea.bvbej.com/bvbej/base-golang/tool"
|
||||
"github.com/gorilla/websocket"
|
||||
"go.uber.org/zap"
|
||||
"net/http"
|
||||
|
@ -2,8 +2,8 @@ package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/websocket/codec"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/websocket/util"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/websocket/codec"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/websocket/util"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
@ -5,7 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/websocket/codec"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/websocket/codec"
|
||||
)
|
||||
|
||||
type jsonCodec struct{}
|
||||
|
@ -4,8 +4,8 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/websocket/codec"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/websocket/codec/protobuf/protocol"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/websocket/codec"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/websocket/codec/protobuf/protocol"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
|
@ -4,14 +4,14 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/mux"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/mux"
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/websocket/peer"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/websocket/peer"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
|
@ -4,7 +4,7 @@ import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/websocket/peer"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/websocket/peer"
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
|
@ -3,7 +3,7 @@ package service
|
||||
import (
|
||||
"time"
|
||||
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/websocket/peer"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/websocket/peer"
|
||||
)
|
||||
|
||||
type Component interface {
|
||||
|
@ -1,7 +1,7 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/websocket/peer"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/websocket/peer"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
|
@ -2,11 +2,11 @@ package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/websocket/util"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/websocket/util"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/websocket/peer"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/websocket/peer"
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
|
@ -2,9 +2,9 @@ package service
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/websocket/codec"
|
||||
_ "git.bvbej.com/bvbej/base-golang/pkg/websocket/codec/json"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/websocket/peer"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/websocket/codec"
|
||||
_ "gitea.bvbej.com/bvbej/base-golang/pkg/websocket/codec/json"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/websocket/peer"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/websocket/peer"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/websocket/peer"
|
||||
)
|
||||
|
||||
type callBackEntity struct{}
|
||||
|
@ -5,6 +5,7 @@ import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/spf13/cast"
|
||||
"io"
|
||||
"math"
|
||||
"math/rand"
|
||||
@ -33,7 +34,7 @@ func GetOrderNumber() string {
|
||||
// ByteFmt 格式化显示文件大小
|
||||
func ByteFmt(size int64) string {
|
||||
var unitArr = []string{"B", "KB", "MB", "GB", "TB", "EB"}
|
||||
if size == 0 {
|
||||
if size <= 0 {
|
||||
return "unknown"
|
||||
}
|
||||
fs := float64(size)
|
||||
@ -43,7 +44,7 @@ func ByteFmt(size int64) string {
|
||||
if frac > 0 {
|
||||
return fmt.Sprintf("%.1f%s", math.Floor(val*10)/10, unitArr[p])
|
||||
} else {
|
||||
return fmt.Sprintf("%d%s", int(val), unitArr[p])
|
||||
return fmt.Sprintf("%d%s", cast.ToInt(val), unitArr[p])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,10 +2,10 @@ package tool
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/aes"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/hmac"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/httpclient"
|
||||
"git.bvbej.com/bvbej/base-golang/pkg/time_parse"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/aes"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/hmac"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/httpclient"
|
||||
"gitea.bvbej.com/bvbej/base-golang/pkg/time_parse"
|
||||
"github.com/tidwall/gjson"
|
||||
netUrl "net/url"
|
||||
"strconv"
|
||||
|
Reference in New Issue
Block a user