Retrieving File Metadata with os.Stat()
The os.Stat() function returns a FileInfo interface containing metadata about a file:
info, err := os.Stat(filename)
The FileInfo interface defines the following methods:
type FileInfo interface {
Name() string // base name of the file
Size() int64 // length in bytes for regular files
Mode() FileMode // file mode bits
ModTime() time.Time // modification time
IsDir() bool // abbreviation for Mode().IsDir()
Sys() any // underlying data source (can return nil)
}
Overview
This implementation provides a TCP-based file transfer solution consisting of two components:
- Sender: Reads a file from disk and transmits it over the network
- Receiver: Listens for incoming connections, receives file data, and writes it to disk
Sender Implementation
package main
import (
"fmt"
"io"
"net"
"os"
)
func transmitFile(filePath string, connection net.Conn) {
file, openErr := os.Open(filePath)
if openErr != nil {
fmt.Println("Failed to open file:", openErr)
return
}
defer file.Close()
buffer := make([]byte, 4096)
for {
bytesRead, readErr := file.Read(buffer)
if readErr != nil {
if readErr == io.EOF {
fmt.Println("File transmission complete")
} else {
fmt.Println("Read error:", readErr)
}
return
}
connection.Write(buffer[:bytesRead])
}
}
func main() {
fmt.Println("Enter the file path to transfer:")
var filePath string
fmt.Scan(&filePath)
fileInfo, statErr := os.Stat(filePath)
if statErr != nil {
fmt.Println("Stat error:", statErr)
return
}
connection, dialErr := net.Dial("tcp", "127.0.0.1:8080")
if dialErr != nil {
fmt.Println("Connection error:", dialErr)
return
}
defer connection.Close()
_, writeErr := connection.Write([]byte(fileInfo.Name()))
if writeErr != nil {
fmt.Println("Write error:", writeErr)
return
}
responseBuffer := make([]byte, 1024)
bytesRead, readErr := connection.Read(responseBuffer)
if readErr != nil {
fmt.Println("Read error:", readErr)
return
}
if string(responseBuffer[:bytesRead]) == "READY" {
transmitFile(filePath, connection)
}
}
Workflow:
- Prompt user for file path input
- Retrieve file metadata using
os.Stat() - Establish TCP connection to server
- Send filename to receiver
- Wait for acknowledgment response
- Stream file contents in chunks
Receiver Implementation
package main
import (
"fmt"
"io"
"net"
"os"
)
func receiveFile(filename string, connection net.Conn) {
outputFile, createErr := os.Create(filename)
if createErr != nil {
fmt.Println("File creation error:", createErr)
return
}
defer outputFile.Close()
buffer := make([]byte, 4096)
for {
bytesRead, readErr := connection.Read(buffer)
if readErr != nil {
if readErr == io.EOF {
fmt.Println("File reception complete")
} else {
fmt.Println("Connection read error:", readErr)
}
return
}
outputFile.Write(buffer[:bytesRead])
}
}
func main() {
listener, listenErr := net.Listen("tcp", "127.0.0.1:8080")
if listenErr != nil {
fmt.Println("Listen error:", listenErr)
return
}
defer listener.Close()
connection, acceptErr := listener.Accept()
if acceptErr != nil {
fmt.Println("Accept error:", acceptErr)
return
}
defer connection.Close()
receiveBuffer := make([]byte, 1024)
bytesRead, readErr := connection.Read(receiveBuffer)
if readErr != nil {
fmt.Println("Read error:", readErr)
return
}
receivedFilename := string(receiveBuffer[:bytesRead])
connection.Write([]byte("READY"))
receiveFile(receivedFilename, connection)
}
Workflow:
- Create TCP listener on specified address
- Accept incoming client connection
- Read filename from sender
- Send acknowledgment signal
- Continuously receive data and write to fille
Transfer Process
The communication follows this sequence:
- Sender connnects to receiver on port 8080
- Sender transmits the filename
- Receiver creates local file with same name
- Receiver sends "READY" acknowledgment
- Sender streams file contents in 4KB chunks
- Receiver writes each chunk to disk until EOF
Both implementations use buffered I/O with 4KB chunks for efficient network transfer. The receiver creates the file with the same name as the original, preserving the filename while the content is copied exact.