【问题标题】:Extract filename from io.ReadCloser从 io.ReadCloser 中提取文件名
【发布时间】:2021-08-31 20:19:12
【问题描述】:

我需要获取从前端接收后端的某些文件的文件名。后端(在 Go 中实现)将接收文件为io.ReadCloser。有没有办法从io.ReadCloser 中提取它?

【问题讨论】:

  • 取决于底层类型。 io.ReadCloser 接口本身并没有描述这些信息。 Steven Penny 的回答举了一个例子。
  • 它显示为*runtime.File,不能转换为*os.File

标签: go go-swagger


【解决方案1】:

您必须使用 Name 方法将其强制转换为类型:

package main

import (
   "io"
   "os"
)

func open(name string) (io.ReadCloser, error) {
   return os.Open(name)
}

func main() {
   c, e := open("file.txt")
   if e != nil {
      panic(e)
   }
   defer c.Close()
   f := c.(*os.File)
   println(f.Name())
}

【讨论】:

    【解决方案2】:

    通过定义一个嵌入 io.Reader 的接口,您可以预先要求 Name() 方法:

    package main
    
    import (
        "fmt"
        "io"
        "log"
        "os"
    )
    
    type NamedReadCloser interface {
        io.ReadCloser
        Name() string
    }
    
    func doThings(f NamedReadCloser) error {
        defer f.Close()
        b, err := io.ReadAll(f)
        if err != nil {
            return err
        }
        fmt.Printf("Name: %s, Content: %s\n", f.Name(), b)
        return nil
    }
    
    func main() {
        f, err := os.Open("/etc/hosts")
        if err != nil {
            log.Fatal("Cannot open file: ", err)
        }
        err = doThings(f)
        if err != nil {
            log.Fatal("Error doing things: ", err)
        }
    }
    

    这仅在传入的内容具有名称方法时才有效,例如*os.File。如果没有,那么您尝试做的事情是不可能的。

    【讨论】:

      【解决方案3】:
      package main
      
      import (
          "errors"
          "fmt"
          "io"
          "os"
      )
      
      func fatalln(err error) {
          fmt.Fprintln(os.Stderr, err)
          os.Exit(1)
      }
      
      // hasName interface is an interface that expects types
      // that implements it to have "Name() string" method.
      type hasName interface {
          Name() string
      }
      
      func open(name string) (io.ReadCloser, error) {
          f, err := os.Open(name)
          if err != nil {
              return nil, err
          }
          // f implements io.ReadCloser interface as *os.File
          // has Read and Close methods.
          return f, nil
      }
      
      func main() {
          // rc is of the type io.ReadCloser
          rc, err := open("example.txt")
          if err != nil {
              fatalln(err)
          }
          defer rc.Close()
      
          // Type assetion to check rc's underlying type has
          // a method "Name() string".
          f, ok := rc.(hasName)
          if !ok {
              fatalln(errors.New("type assertion failed"))
          }
      
          // Yay, type assertion succeeded. Print the name!
          fmt.Println("Name:", f.Name())
      }
      

      【讨论】:

      • 但正如@Volker 在他的回答中所解释的那样,除非您只知道它是io.ReadCloser,否则您根本做不到。所以,照顾好它。我的回答只是探索了一种方法,可能有很多方法可以做到这一点——我希望底层类型应该有一个名为“Name”的方法,它可能会发生底层类型有一个包含文件名的字段。
      • 虽然我认为实现 Name 方法和 io.ReadCloserName 方法的结构可能与文件名无关(甚至在它的内部使用),这实际上是一个相当平衡的尝试;但是,我认为如果有一些错误检测机制会更好,但这需要更多的域逻辑。
      【解决方案4】:

      后端(在 Go 中实现)将接收文件作为 io.ReadCloser。有没有办法从 io.ReadCloser 中提取它?

      没有。

      通过运行go doc io.ReadCloser 查看 io.ReadCloser 提供了哪些方法,并注意没有提供名称的方法。因此,除非您不知道它是一个 io.ReadCloser,否则您根本无法做到。

      【讨论】:

        【解决方案5】:

        这里的io.ReadCloser 是运行时阅读器的阅读器,它在前端将文件发送到后端时从网络读取文件。您必须根据请求本身来获取该文件名。 这是一个假设,但在大多数文件上传的情况下,请求是多部分请求。如果你有同样的情况,你可以阅读标题,通常是Content-Disposition 来识别文件类型。 Go native http.Request 具有解析细节的能力。你可以试试这个:

        formFile, handler, err := r.FormFile("file")  // read file from network with key "file"
        defer formFile.Close()
        
        fileName := handler.Filename // Get file name
        

        【讨论】:

        • 在此处使用处理程序之前应处理错误。否则可以有panic - nil pointer dereference
        猜你喜欢
        • 2018-09-02
        • 2015-03-19
        • 1970-01-01
        • 1970-01-01
        • 2014-10-04
        • 2016-06-19
        • 2018-10-24
        • 1970-01-01
        相关资源
        最近更新 更多