main.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. package main
  2. import (
  3. "io/ioutil"
  4. "net"
  5. "net/http"
  6. _ "net/http/pprof"
  7. "os"
  8. "path/filepath"
  9. "sort"
  10. "strconv"
  11. "strings"
  12. "webuploader/static"
  13. "github.com/gin-gonic/gin"
  14. "github.com/quillaja/logrus-systemd-formatter/systemdfmt"
  15. log "github.com/sirupsen/logrus"
  16. "github.com/urfave/cli/v2"
  17. )
  18. var (
  19. dir string = getCurrentAbPathByExecutable() + string(filepath.Separator)
  20. port string
  21. group1 *gin.RouterGroup
  22. )
  23. func init() {
  24. gin.SetMode(gin.ReleaseMode)
  25. log.SetFormatter(&systemdfmt.Formatter{})
  26. log.SetOutput(os.Stdout)
  27. //log.SetLevel(log.WarnLevel)
  28. //pprof
  29. go func() {
  30. _ = http.ListenAndServe(":6066", nil)
  31. }()
  32. }
  33. // 获取当前执行文件绝对路径
  34. func getCurrentAbPathByExecutable() string {
  35. exePath, err := os.Executable()
  36. if err != nil {
  37. log.Fatal(err)
  38. }
  39. res, _ := filepath.EvalSymlinks(filepath.Dir(exePath))
  40. return res
  41. }
  42. func getClientIp() {
  43. addrs, err := net.InterfaceAddrs()
  44. if err != nil {
  45. log.Errorf("Error getClientIp %v", err)
  46. return
  47. }
  48. for _, address := range addrs {
  49. if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
  50. if ipnet.IP.To4() != nil {
  51. //return ipnet.IP.String(), nil
  52. log.Infof("HTTP://" + ipnet.IP.String() + ":" + port)
  53. }
  54. }
  55. }
  56. }
  57. func walkDir(filestr string) ([]string, error) {
  58. files, err := ioutil.ReadDir(filestr) // files为当前目录下的所有文件名称【包括文件夹】
  59. if err != nil {
  60. return nil, err
  61. }
  62. var allfile []string
  63. for _, v := range files {
  64. if v.Name() == ".DS_Store" {
  65. continue
  66. }
  67. fullPath := filestr + string(filepath.Separator) + v.Name() // 全路径 + 文件名称
  68. if v.IsDir() { // 如果是目录
  69. a, _ := walkDir(fullPath) // 遍历改路径下的所有文件
  70. allfile = append(allfile, a...)
  71. } else {
  72. allfile = append(allfile, fullPath) // 如果不是文件夹,就直接追加到路径下
  73. }
  74. }
  75. return allfile, nil
  76. }
  77. // 验证是否上传过该文件
  78. func check(c *gin.Context) {
  79. fileMd5 := c.PostForm("md5File")
  80. path := dir + "wisemodel" + string(filepath.Separator) + "merge" + string(filepath.Separator) + fileMd5
  81. if _, err := os.Stat(path); os.IsNotExist(err) { //判断是否已经上传过该文件,设置返回code 为 -1 :秒传
  82. log.Println("mkDir " + path)
  83. _ = os.MkdirAll(path, os.ModePerm)
  84. c.JSON(http.StatusOK, gin.H{
  85. "resultCode": 0, //
  86. })
  87. return
  88. }
  89. result := 0
  90. // 获取目录里上传文件数量
  91. files, err := ioutil.ReadDir(path)
  92. if err != nil {
  93. log.Infof("Error ioutil.ReadDir %v", err)
  94. } else if len(files) == 0 { //文件没有上传过,下标为零
  95. result = 0
  96. } else {
  97. result = 1
  98. }
  99. c.JSON(http.StatusOK, gin.H{
  100. "resultCode": result,
  101. })
  102. }
  103. // 验证是否上传过该文件
  104. func checkChunk(c *gin.Context) {
  105. fileMd5 := c.PostForm("md5File")
  106. chunk := c.PostForm("chunk")
  107. log.Println(fileMd5)
  108. path := dir + "wisemodel" + string(filepath.Separator) + fileMd5 + string(filepath.Separator) + strings.Trim(chunk, "")
  109. log.Println(path)
  110. if _, err := os.Stat(path); err == nil { //判断是否已经上传过该文件,设置返回code 为 -1 :秒传
  111. c.JSON(http.StatusOK, gin.H{
  112. "resultCode": 1, //
  113. })
  114. return
  115. } else {
  116. c.JSON(http.StatusOK, gin.H{
  117. "resultCode": 0,
  118. })
  119. }
  120. }
  121. // 上传文件分片
  122. func upload(c *gin.Context) {
  123. fileName := c.PostForm("name") // 获取文件名
  124. chunkIndex := strings.Trim(c.PostForm("chunk"), "") // 获取分片下标
  125. fileMd5 := c.PostForm("md5File") //获取文件md5
  126. dst := dir + "wisemodel" + string(filepath.Separator) + fileMd5
  127. if chunkIndex == "" {
  128. chunkIndex = "0"
  129. }
  130. result := 0
  131. defer func(subResult *int) {
  132. c.JSON(http.StatusOK, gin.H{
  133. "resultCode": *subResult, //
  134. })
  135. }(&result)
  136. if _, err := os.Stat(dst); os.IsNotExist(err) { //判断是否存在
  137. _ = os.MkdirAll(dst, os.ModePerm)
  138. }
  139. if file, err := c.FormFile("file"); err == nil { //获取分片文件
  140. if chunkIndex == "" {
  141. err = c.SaveUploadedFile(file, dst+string(filepath.Separator)+fileName)
  142. } else {
  143. err = c.SaveUploadedFile(file, dst+string(filepath.Separator)+chunkIndex)
  144. }
  145. if err != nil {
  146. result = 0
  147. log.Errorf("Error c.SaveUploadedFile %v", err)
  148. } else {
  149. result = 1
  150. }
  151. } else {
  152. result = 0
  153. log.Errorf("Error c.FormFile %v", err)
  154. }
  155. }
  156. // 合并分片
  157. func merge(c *gin.Context) {
  158. fileName := c.PostForm("name")
  159. fileMd5 := c.PostForm("md5File")
  160. chunks := c.PostForm("chunks")
  161. path := dir + "wisemodel" + string(filepath.Separator) + fileMd5 //分片存储位置
  162. result := 0
  163. defer func(subResult *int) {
  164. c.JSON(http.StatusOK, gin.H{
  165. "resultCode": *subResult, //
  166. })
  167. }(&result)
  168. // 获取目录里上传文件分片数量
  169. files, err := ioutil.ReadDir(path)
  170. if err != nil || len(files) == 0 || chunks != strconv.Itoa(len(files)) {
  171. log.Errorf("Error merge ioutil.ReadDir %v", err)
  172. result = 0
  173. return
  174. }
  175. //按名字排序
  176. sort.SliceStable(files, func(i, j int) bool {
  177. numA, _ := strconv.Atoi(files[i].Name())
  178. numB, _ := strconv.Atoi(files[j].Name())
  179. return numA < numB
  180. //return files[i].Name() < files[j].Name()
  181. })
  182. realFilePath := dir + "wisemodel" + string(filepath.Separator) + "merge" + string(filepath.Separator) + fileMd5
  183. _ = os.MkdirAll(realFilePath, os.ModePerm) //创建目录
  184. // 创建一个需要合并的文件
  185. realFile, err := os.OpenFile(realFilePath+string(filepath.Separator)+fileName, os.O_CREATE|os.O_TRUNC|os.O_RDWR, os.ModePerm)
  186. defer realFile.Close()
  187. if err != nil {
  188. result = 0
  189. log.Errorf("Error merge os.OpenFile %v", err)
  190. return
  191. }
  192. for _, v := range files { //
  193. f, err := os.OpenFile(path+string(filepath.Separator)+v.Name(), os.O_RDONLY, os.ModePerm)
  194. if err != nil {
  195. result = 0
  196. log.Errorf("Error for os.OpenFile %v", err)
  197. break
  198. }
  199. b, err := ioutil.ReadAll(f)
  200. if err != nil {
  201. result = 0
  202. log.Errorf("Error for ioutil.ReadAll %v", err)
  203. break
  204. }
  205. realFile.Write(b)
  206. // 关闭分片
  207. f.Close()
  208. os.Remove(f.Name()) //合并后,删除分片
  209. result = 1
  210. }
  211. }
  212. // 文件list
  213. func getFiles(c *gin.Context) {
  214. pathMerge := dir + "wisemodel" + string(filepath.Separator) + "merge" //上传文件存储位置
  215. pathLocal := dir + "wisemodel" + string(filepath.Separator) + "local" //本地文件存储位置
  216. if _, err := os.Stat(pathLocal); os.IsNotExist(err) {
  217. _ = os.MkdirAll(pathLocal, os.ModePerm)
  218. }
  219. mergeFiles, err := walkDir(pathMerge)
  220. if err != nil {
  221. log.Errorf("Error walkDir(pathMerge) %v", err)
  222. }
  223. localFiles, err := walkDir(pathLocal)
  224. if err != nil {
  225. log.Errorf("Error walkDir(pathMerge) %v", err)
  226. }
  227. c.JSON(http.StatusOK, gin.H{
  228. "mergeFiles": mergeFiles,
  229. "localFiles": localFiles,
  230. })
  231. }
  232. func doWork() {
  233. getClientIp()
  234. r := gin.Default()
  235. //加载静态文件
  236. r.StaticFS("/static", http.FS(static.FS))
  237. // 最大文件大小M
  238. r.MaxMultipartMemory = 8 << 20
  239. // 跳转上传页面
  240. r.GET("/", func(c *gin.Context) {
  241. c.Redirect(http.StatusMovedPermanently, "/static/upload.html")
  242. })
  243. //路由分组
  244. group1 = r.Group("/bigfile")
  245. group1.POST("/check", check)
  246. group1.POST("/checkChunk", checkChunk)
  247. group1.POST("/upload", upload)
  248. group1.POST("/merge", merge)
  249. group1.GET("/getFiles", getFiles)
  250. group1.GET("/down", func(c *gin.Context) {
  251. pathName := c.DefaultQuery("path", "")
  252. fileName := filepath.Base(pathName)
  253. if pathName != "" {
  254. c.Header("Content-Type", "application/octet-stream")
  255. c.Header("Content-Disposition", "attachment; filename="+fileName)
  256. c.Header("Content-Disposition", "inline;filename="+fileName)
  257. c.Header("Content-Transfer-Encoding", "binary")
  258. c.Header("Cache-Control", "no-cache")
  259. c.File(pathName)
  260. }
  261. })
  262. _ = r.Run(":" + port)
  263. }
  264. func main() {
  265. app := &cli.App{
  266. Name: "webuploader", //app名字
  267. Usage: "文件服务", //详细描述该app的用途
  268. // 设置参数列表
  269. Flags: []cli.Flag{
  270. &cli.StringFlag{
  271. Name: "port",
  272. Value: "8888",
  273. Aliases: []string{"p"},
  274. Usage: "port",
  275. Destination: &port,
  276. },
  277. },
  278. Action: func(context *cli.Context) error {
  279. doWork()
  280. return nil
  281. },
  282. }
  283. if err := app.Run(os.Args); err != nil {
  284. log.Errorln("app run error: ", err.Error())
  285. os.Exit(1)
  286. }
  287. }