123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 |
- package main
- import (
- "io/ioutil"
- "net"
- "net/http"
- _ "net/http/pprof"
- "os"
- "path/filepath"
- "sort"
- "strconv"
- "strings"
- "webuploader/static"
- "github.com/gin-gonic/gin"
- "github.com/quillaja/logrus-systemd-formatter/systemdfmt"
- log "github.com/sirupsen/logrus"
- "github.com/urfave/cli/v2"
- )
- var (
- dir string = getCurrentAbPathByExecutable() + string(filepath.Separator)
- port string
- group1 *gin.RouterGroup
- )
- func init() {
- gin.SetMode(gin.ReleaseMode)
- log.SetFormatter(&systemdfmt.Formatter{})
- log.SetOutput(os.Stdout)
- //log.SetLevel(log.WarnLevel)
- //pprof
- go func() {
- _ = http.ListenAndServe(":6066", nil)
- }()
- }
- // 获取当前执行文件绝对路径
- func getCurrentAbPathByExecutable() string {
- exePath, err := os.Executable()
- if err != nil {
- log.Fatal(err)
- }
- res, _ := filepath.EvalSymlinks(filepath.Dir(exePath))
- return res
- }
- func getClientIp() {
- addrs, err := net.InterfaceAddrs()
- if err != nil {
- log.Errorf("Error getClientIp %v", err)
- return
- }
- for _, address := range addrs {
- if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
- if ipnet.IP.To4() != nil {
- //return ipnet.IP.String(), nil
- log.Infof("HTTP://" + ipnet.IP.String() + ":" + port)
- }
- }
- }
- }
- func walkDir(filestr string) ([]string, error) {
- files, err := ioutil.ReadDir(filestr) // files为当前目录下的所有文件名称【包括文件夹】
- if err != nil {
- return nil, err
- }
- var allfile []string
- for _, v := range files {
- if v.Name() == ".DS_Store" {
- continue
- }
- fullPath := filestr + string(filepath.Separator) + v.Name() // 全路径 + 文件名称
- if v.IsDir() { // 如果是目录
- a, _ := walkDir(fullPath) // 遍历改路径下的所有文件
- allfile = append(allfile, a...)
- } else {
- allfile = append(allfile, fullPath) // 如果不是文件夹,就直接追加到路径下
- }
- }
- return allfile, nil
- }
- // 验证是否上传过该文件
- func check(c *gin.Context) {
- fileMd5 := c.PostForm("md5File")
- path := dir + "wisemodel" + string(filepath.Separator) + "merge" + string(filepath.Separator) + fileMd5
- if _, err := os.Stat(path); os.IsNotExist(err) { //判断是否已经上传过该文件,设置返回code 为 -1 :秒传
- log.Println("mkDir " + path)
- _ = os.MkdirAll(path, os.ModePerm)
- c.JSON(http.StatusOK, gin.H{
- "resultCode": 0, //
- })
- return
- }
- result := 0
- // 获取目录里上传文件数量
- files, err := ioutil.ReadDir(path)
- if err != nil {
- log.Infof("Error ioutil.ReadDir %v", err)
- } else if len(files) == 0 { //文件没有上传过,下标为零
- result = 0
- } else {
- result = 1
- }
- c.JSON(http.StatusOK, gin.H{
- "resultCode": result,
- })
- }
- // 验证是否上传过该文件
- func checkChunk(c *gin.Context) {
- fileMd5 := c.PostForm("md5File")
- chunk := c.PostForm("chunk")
- log.Println(fileMd5)
- path := dir + "wisemodel" + string(filepath.Separator) + fileMd5 + string(filepath.Separator) + strings.Trim(chunk, "")
- log.Println(path)
- if _, err := os.Stat(path); err == nil { //判断是否已经上传过该文件,设置返回code 为 -1 :秒传
- c.JSON(http.StatusOK, gin.H{
- "resultCode": 1, //
- })
- return
- } else {
- c.JSON(http.StatusOK, gin.H{
- "resultCode": 0,
- })
- }
- }
- // 上传文件分片
- func upload(c *gin.Context) {
- fileName := c.PostForm("name") // 获取文件名
- chunkIndex := strings.Trim(c.PostForm("chunk"), "") // 获取分片下标
- fileMd5 := c.PostForm("md5File") //获取文件md5
- dst := dir + "wisemodel" + string(filepath.Separator) + fileMd5
- if chunkIndex == "" {
- chunkIndex = "0"
- }
- result := 0
- defer func(subResult *int) {
- c.JSON(http.StatusOK, gin.H{
- "resultCode": *subResult, //
- })
- }(&result)
- if _, err := os.Stat(dst); os.IsNotExist(err) { //判断是否存在
- _ = os.MkdirAll(dst, os.ModePerm)
- }
- if file, err := c.FormFile("file"); err == nil { //获取分片文件
- if chunkIndex == "" {
- err = c.SaveUploadedFile(file, dst+string(filepath.Separator)+fileName)
- } else {
- err = c.SaveUploadedFile(file, dst+string(filepath.Separator)+chunkIndex)
- }
- if err != nil {
- result = 0
- log.Errorf("Error c.SaveUploadedFile %v", err)
- } else {
- result = 1
- }
- } else {
- result = 0
- log.Errorf("Error c.FormFile %v", err)
- }
- }
- // 合并分片
- func merge(c *gin.Context) {
- fileName := c.PostForm("name")
- fileMd5 := c.PostForm("md5File")
- chunks := c.PostForm("chunks")
- path := dir + "wisemodel" + string(filepath.Separator) + fileMd5 //分片存储位置
- result := 0
- defer func(subResult *int) {
- c.JSON(http.StatusOK, gin.H{
- "resultCode": *subResult, //
- })
- }(&result)
- // 获取目录里上传文件分片数量
- files, err := ioutil.ReadDir(path)
- if err != nil || len(files) == 0 || chunks != strconv.Itoa(len(files)) {
- log.Errorf("Error merge ioutil.ReadDir %v", err)
- result = 0
- return
- }
- //按名字排序
- sort.SliceStable(files, func(i, j int) bool {
- numA, _ := strconv.Atoi(files[i].Name())
- numB, _ := strconv.Atoi(files[j].Name())
- return numA < numB
- //return files[i].Name() < files[j].Name()
- })
- realFilePath := dir + "wisemodel" + string(filepath.Separator) + "merge" + string(filepath.Separator) + fileMd5
- _ = os.MkdirAll(realFilePath, os.ModePerm) //创建目录
- // 创建一个需要合并的文件
- realFile, err := os.OpenFile(realFilePath+string(filepath.Separator)+fileName, os.O_CREATE|os.O_TRUNC|os.O_RDWR, os.ModePerm)
- defer realFile.Close()
- if err != nil {
- result = 0
- log.Errorf("Error merge os.OpenFile %v", err)
- return
- }
- for _, v := range files { //
- f, err := os.OpenFile(path+string(filepath.Separator)+v.Name(), os.O_RDONLY, os.ModePerm)
- if err != nil {
- result = 0
- log.Errorf("Error for os.OpenFile %v", err)
- break
- }
- b, err := ioutil.ReadAll(f)
- if err != nil {
- result = 0
- log.Errorf("Error for ioutil.ReadAll %v", err)
- break
- }
- realFile.Write(b)
- // 关闭分片
- f.Close()
- os.Remove(f.Name()) //合并后,删除分片
- result = 1
- }
- }
- // 文件list
- func getFiles(c *gin.Context) {
- pathMerge := dir + "wisemodel" + string(filepath.Separator) + "merge" //上传文件存储位置
- pathLocal := dir + "wisemodel" + string(filepath.Separator) + "local" //本地文件存储位置
- if _, err := os.Stat(pathLocal); os.IsNotExist(err) {
- _ = os.MkdirAll(pathLocal, os.ModePerm)
- }
- mergeFiles, err := walkDir(pathMerge)
- if err != nil {
- log.Errorf("Error walkDir(pathMerge) %v", err)
- }
- localFiles, err := walkDir(pathLocal)
- if err != nil {
- log.Errorf("Error walkDir(pathMerge) %v", err)
- }
- c.JSON(http.StatusOK, gin.H{
- "mergeFiles": mergeFiles,
- "localFiles": localFiles,
- })
- }
- func doWork() {
- getClientIp()
- r := gin.Default()
- //加载静态文件
- r.StaticFS("/static", http.FS(static.FS))
- // 最大文件大小M
- r.MaxMultipartMemory = 8 << 20
- // 跳转上传页面
- r.GET("/", func(c *gin.Context) {
- c.Redirect(http.StatusMovedPermanently, "/static/upload.html")
- })
- //路由分组
- group1 = r.Group("/bigfile")
- group1.POST("/check", check)
- group1.POST("/checkChunk", checkChunk)
- group1.POST("/upload", upload)
- group1.POST("/merge", merge)
- group1.GET("/getFiles", getFiles)
- group1.GET("/down", func(c *gin.Context) {
- pathName := c.DefaultQuery("path", "")
- fileName := filepath.Base(pathName)
- if pathName != "" {
- c.Header("Content-Type", "application/octet-stream")
- c.Header("Content-Disposition", "attachment; filename="+fileName)
- c.Header("Content-Disposition", "inline;filename="+fileName)
- c.Header("Content-Transfer-Encoding", "binary")
- c.Header("Cache-Control", "no-cache")
- c.File(pathName)
- }
- })
- _ = r.Run(":" + port)
- }
- func main() {
- app := &cli.App{
- Name: "webuploader", //app名字
- Usage: "文件服务", //详细描述该app的用途
- // 设置参数列表
- Flags: []cli.Flag{
- &cli.StringFlag{
- Name: "port",
- Value: "8888",
- Aliases: []string{"p"},
- Usage: "port",
- Destination: &port,
- },
- },
- Action: func(context *cli.Context) error {
- doWork()
- return nil
- },
- }
- if err := app.Run(os.Args); err != nil {
- log.Errorln("app run error: ", err.Error())
- os.Exit(1)
- }
- }
|