为了账号安全,请及时绑定邮箱和手机立即绑定

Golang上传整个目录并发返回许多打开的文件

Golang上传整个目录并发返回许多打开的文件

Go
森林海 2021-08-16 16:08:08
我正在尝试将整个目录上传到服务器。它适用于小目录,但有 100 多张图片,它返回“许多打开的文件”错误。我在读取文件后立即关闭该文件。知道如何解决这个问题吗?这是我的代码    func uploadDir(path string) error {    dir, err := os.Open(path)    if err != nil {        return err    }    files, err := dir.Readdirnames(-1)    if err != nil {        return err    }    dir.Close()    errChan := make(chan error)    resChan := make(chan *client.PutResult)    remaining := len(files)    for _, file := range files {        file := file        go func() {            file, err := os.Open(path + "/" + file)            if err != nil {                errChan <- err            }            c := client.NewClient(os.Getenv("DROPS_SERVER"))            res, err := c.Upload(client.NewUploadHandleFromReader(file))            file.Close()            if err != nil {                errChan <- err            }            resChan <- res        }()    }    for {        select {        case res := <-resChan:            log.Println(res)            remaining--        case err := <-errChan:            if err != nil {                return err            }        }        if remaining == 0 {            break        }    }    return nil}
查看完整描述

2 回答

?
慕雪6442864

TA贡献1812条经验 获得超5个赞

原始代码不限制活动 go 例程的数量,因此不限制打开的文件描述符的数量。一些操作系统对打开的文件描述符的数量有限制。解决方法是创建固定数量的工作程序 go 例程。


func uploadDir(path string) error {


    // Read directory and close.


    dir, err := os.Open(path)

    if err != nil {

        return err

    }

    names, err := dir.Readdirnames(-1)

    if err != nil {

        return err

    }

    dir.Close()


    // Copy names to a channel for workers to consume. Close the

    // channel so that workers stop when all work is complete.


    namesChan := make(chan string, len(names))

    for _, name := range names {

        namesChan <- name

    }

    close(namesChan)


    // Create a maximum of 8 workers


    workers := 8

    if len(names) < workers {

        workers = len(names)

    }


    errChan := make(chan error, 1)

    resChan := make(chan *client.PutResult, len(names))


    // Run workers


    for i := 0; i < workers; i++ {

        go func() {

            // Consume work from namesChan. Loop will end when no more work.

            for name := range namesChan {

                file, err := os.Open(filepath.Join(path, name))

                if err != nil {

                    select {

                    case errChan <- err:

                        // will break parent goroutine out of loop

                    default:

                       // don't care, first error wins

                    }

                    return

                }

                c := client.NewClient(os.Getenv("DROPS_SERVER"))

                res, err := c.Upload(client.NewUploadHandleFromReader(file))

                file.Close()

                if err != nil {

                    select {

                    case errChan <- err:

                        // will break parent goroutine out of loop

                    default:

                       // don't care, first error wins

                    }

                    return

                }

                resChan <- res

            }

        }()

    }


    // Collect results from workers 


    for i := 0; i < len(names); i++ {

        select {

        case res := <-resChan:

            log.Println(res)

        case err := <-errChan:

            return err

        }

    }

    return nil

}

作为奖励,我修改了通道大小并发送操作,以便在出现错误时不会卡住 goroutine。


查看完整回答
反对 回复 2021-08-16
  • 2 回答
  • 0 关注
  • 212 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信