-2

I need to get the filename of certain file(s) that receives backend from the frontend. Backend (implemented in Go) will receive the file as io.ReadCloser. Is there way I could extract it from the io.ReadCloser?

Govinda Malavipathirana
  • 1,095
  • 2
  • 11
  • 29

5 Answers5

4

Backend (implemented in Go) will receive the file as io.ReadCloser. Is there way I could extract it from the io.ReadCloser?

No.

Take a look at which methods an io.ReadCloser provides by running go doc io.ReadCloser and note that there isn't a method which will provide a name. So unless you know nothing more that that it is an io.ReadCloser you simply cannot do it.

Volker
  • 40,468
  • 7
  • 81
  • 87
1
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())
}
shmsr
  • 3,802
  • 2
  • 18
  • 29
  • But as explained by @Volker in his answer that unless you know nothing more than it is an `io.ReadCloser`, you simply can't. So, take care of that. My answer just explores one way, there could be plenty of ways of doing it — I expect the underlying type should have a method named "Name" it might happen that the underlying type has a field that has the filename. – shmsr Jun 16 '21 at 05:58
  • While I would argue it is possible for a struct that implement a `Name` method and `io.ReadCloser`'s `Name` method to have nothing to do with filename (or even has any meaning outside of its internal use), this is actually a reasonably balanced try; however, I think it would be better if there is some error detecting mechanisms, but those would require more domain logic. – leaf bebop Jun 16 '21 at 09:18
0

The io.ReadCloser here is a reader for runtime reader which reads file from network as the frontend sends it to backend. You'll have to work on request itself to get that file name. This is an assumption but in most such cases for file upload, the request is a multipart request. If you have the same situation, you can read the headers, typically Content-Disposition to identify the file type. Go native http.Request has ability to parse the details. You can try this :

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

fileName := handler.Filename // Get file name
advay rajhansa
  • 1,129
  • 9
  • 17
  • error should be handled before using handler here. Otherwise there can be `panic - nil pointer dereference` . – nipuna Jun 16 '21 at 04:40
0

By defining an interface which embeds io.Reader you can require a Name() method up front:

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)
    }
}

This will only work if what is passed in has a name method, like an *os.File. If it does not, then what you are trying to do is not possible.

Bracken
  • 989
  • 8
  • 21
-1

You'll have to cast it to a type with a Name method:

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())
}
Nimantha
  • 6,405
  • 6
  • 28
  • 69
Zombo
  • 1
  • 62
  • 391
  • 407