完成packer
This commit is contained in:
commit
e57ee27b95
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
// 使用 IntelliSense 了解相关属性。
|
||||||
|
// 悬停以查看现有属性的描述。
|
||||||
|
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Launch Package",
|
||||||
|
"type": "go",
|
||||||
|
"request": "launch",
|
||||||
|
"mode": "auto",
|
||||||
|
"program": "${fileDirname}",
|
||||||
|
"args":[
|
||||||
|
"-z",
|
||||||
|
"-b",
|
||||||
|
"exmaple/example",
|
||||||
|
"-f",
|
||||||
|
"exmaple/res.text",
|
||||||
|
"-f",
|
||||||
|
"exmaple/compress.text",
|
||||||
|
"-o",
|
||||||
|
"exmaple/packedexample"
|
||||||
|
],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Makefile for building the packer command-line tool
|
||||||
|
|
||||||
|
# Variables
|
||||||
|
BINARY_NAME := packer
|
||||||
|
BUILD_DIR := ./bin
|
||||||
|
SRC_DIR := ./cmd/packer
|
||||||
|
|
||||||
|
# Build the packer tool
|
||||||
|
build:
|
||||||
|
@echo "Building $(BINARY_NAME)..."
|
||||||
|
@mkdir -p $(BUILD_DIR)
|
||||||
|
@go build -o $(BUILD_DIR)/$(BINARY_NAME) $(SRC_DIR)
|
||||||
|
@echo "Build completed. Binary saved to $(BUILD_DIR)/$(BINARY_NAME)"
|
||||||
|
|
||||||
|
# Clean build artifacts
|
||||||
|
clean:
|
||||||
|
@echo "Cleaning build artifacts..."
|
||||||
|
@rm -rf $(BUILD_DIR)
|
||||||
|
@echo "Clean completed."
|
||||||
|
|
||||||
|
.PHONY: build clean
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CountWriter struct {
|
||||||
|
Writer io.Writer
|
||||||
|
Count int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cw *CountWriter) Write(p []byte) (n int, err error) {
|
||||||
|
n, err = cw.Writer.Write(p)
|
||||||
|
cw.Count += int64(n)
|
||||||
|
return n, err
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,198 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
|
||||||
|
"compress/flate"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"git.kingecg.top/kingecg/gopacker/data"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type stringSlice []string
|
||||||
|
|
||||||
|
// 实现 flag.Value 接口
|
||||||
|
func (s *stringSlice) String() string {
|
||||||
|
return strings.Join(*s, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *stringSlice) Set(value string) error {
|
||||||
|
*s = append(*s, value)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func addFile(bootPathDir string, filePath string, outputFile *os.File, fs *data.FileSystem) error {
|
||||||
|
relPath, err := filepath.Rel(bootPathDir, filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
relPath = strings.ReplaceAll(relPath, "\\\\", "/")
|
||||||
|
file, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
fileInfo, err := file.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Get current offset in output file
|
||||||
|
offset, err := outputFile.Seek(0, io.SeekCurrent)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// Copy file content to output
|
||||||
|
zippedSize := int64(0)
|
||||||
|
if needZip {
|
||||||
|
c := &CountWriter{Writer: outputFile}
|
||||||
|
zipWriter, err := flate.NewWriter(c, flate.BestCompression)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = io.Copy(zipWriter, file)
|
||||||
|
//
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = zipWriter.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
zippedSize = c.Count
|
||||||
|
// fmt.Println("zippedSize:", zippedSize)
|
||||||
|
} else {
|
||||||
|
_, err = io.Copy(outputFile, file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record file info in FileSystem
|
||||||
|
fs.Files[relPath] = data.File{
|
||||||
|
Size: fileInfo.Size(),
|
||||||
|
ZSize: zippedSize,
|
||||||
|
Offset: offset,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var needZip bool
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var filePaths stringSlice
|
||||||
|
bootPath := flag.String("b", "", "Path to the boot program")
|
||||||
|
flag.BoolVar(&needZip, "z", false, "Whether to zip the output file")
|
||||||
|
flag.Var(&filePaths, "f", "Paths to resource files or directories")
|
||||||
|
outputPath := flag.String("o", "", "Path to the output file")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *bootPath == "" || *outputPath == "" {
|
||||||
|
fmt.Println("Error: boot path (-b) and output path (-o) are required")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open boot file
|
||||||
|
bootFile, err := os.Open(*bootPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error opening boot file: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
defer bootFile.Close()
|
||||||
|
|
||||||
|
bootPathDir := filepath.Dir(*bootPath)
|
||||||
|
// Create output file
|
||||||
|
outputFile, err := os.Create(*outputPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error creating output file: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
defer outputFile.Close()
|
||||||
|
|
||||||
|
// Copy boot file to output
|
||||||
|
_, err = io.Copy(outputFile, bootFile)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error copying boot file: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize FileSystem
|
||||||
|
fs := data.FileSystem{
|
||||||
|
Files: make(map[string]data.File),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process resource files
|
||||||
|
fps := []string(filePaths)
|
||||||
|
for _, filePath := range fps {
|
||||||
|
fileInfo, err := os.Stat(filePath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error accessing resource file: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fileInfo.IsDir() {
|
||||||
|
// Handle directory
|
||||||
|
err := filepath.Walk(filePath, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !info.IsDir() {
|
||||||
|
err := addFile(bootPathDir, path, outputFile, &fs)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error processing file: %v\n", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error processing directory: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Handle single file
|
||||||
|
err := addFile(bootPathDir, filePath, outputFile, &fs)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error processing file: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize FileSystem to JSON
|
||||||
|
jsonData, err := fs.ToJSON()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error serializing FileSystem: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write JSON data to output file
|
||||||
|
jsonBytes := []byte(jsonData)
|
||||||
|
_, err = outputFile.Write(jsonBytes)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error writing JSON data: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write JSON data length (8 bytes)
|
||||||
|
jsonLen := uint64(len(jsonBytes))
|
||||||
|
err = binary.Write(outputFile, binary.LittleEndian, jsonLen)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error writing JSON length: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Packaging completed successfully!")
|
||||||
|
|
||||||
|
// change file mod to be executable
|
||||||
|
err = os.Chmod(*outputPath, 0755)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error setting executable permission: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Set executable permission successfully!")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,167 @@
|
||||||
|
package data
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/flate"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type File struct {
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
ZSize int64 `json:"zsize"`
|
||||||
|
Offset int64 `json:"offset"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *File) Read(pf *os.File, b []byte) (int, error) {
|
||||||
|
if f.ZSize > 0 {
|
||||||
|
compressed := make([]byte, f.ZSize)
|
||||||
|
_, err := pf.ReadAt(compressed, f.Offset)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
// Decompress
|
||||||
|
reader := flate.NewReader(bytes.NewReader(compressed))
|
||||||
|
defer reader.Close()
|
||||||
|
n, err := reader.Read(b)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
fmt.Println("Error reading file:", err, n, compressed)
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return pf.ReadAt(b, f.Offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
type FileSystem struct {
|
||||||
|
Files map[string]File `json:"files"`
|
||||||
|
fp *os.File
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *FileSystem) ToJSON() (string, error) {
|
||||||
|
data, err := json.Marshal(fs)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to marshal FileSystem to JSON: %v", err)
|
||||||
|
}
|
||||||
|
return string(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromJSON(jsonStr string) (*FileSystem, error) {
|
||||||
|
var fs FileSystem
|
||||||
|
err := json.Unmarshal([]byte(jsonStr), &fs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to unmarshal JSON to FileSystem: %v", err)
|
||||||
|
}
|
||||||
|
return &fs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *FileSystem) AddFile(name string, size int64, offset int64) {
|
||||||
|
if fs.Files == nil {
|
||||||
|
fs.Files = make(map[string]File)
|
||||||
|
}
|
||||||
|
fs.Files[name] = File{Size: size, Offset: offset}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *FileSystem) GetFile(name string) (File, bool) {
|
||||||
|
file, exists := fs.Files[name]
|
||||||
|
return file, exists
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *FileSystem) ListFiles() []string {
|
||||||
|
keys := make([]string, 0, len(fs.Files))
|
||||||
|
for k := range fs.Files {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fs *FileSystem) ReadFile(name string) ([]byte, int, error) {
|
||||||
|
err := fs.openContainerFile()
|
||||||
|
if err != nil {
|
||||||
|
return []byte{}, 0, fmt.Errorf("failed to open container file: %v", err)
|
||||||
|
}
|
||||||
|
file, exists := fs.GetFile(name)
|
||||||
|
if !exists {
|
||||||
|
return []byte{}, 0, fmt.Errorf("file %s not found", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
b := make([]byte, file.Size)
|
||||||
|
len, err := file.Read(fs.fp, b)
|
||||||
|
return b, len, err
|
||||||
|
}
|
||||||
|
func (fs *FileSystem) openContainerFile() error {
|
||||||
|
if fs.fp != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
exePath, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get executable path: %v", err)
|
||||||
|
}
|
||||||
|
fp, err := os.Open(exePath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to open executable file: %v", err)
|
||||||
|
}
|
||||||
|
fs.fp = fp
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (fs *FileSystem) Close() error {
|
||||||
|
if fs.fp != nil {
|
||||||
|
err := fs.fp.Close()
|
||||||
|
fs.fp = nil
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFileSystem() (*FileSystem, error) {
|
||||||
|
exePath, err := os.Executable()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get executable path: %v", err)
|
||||||
|
}
|
||||||
|
fp, err := os.Open(exePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to open executable file: %v", err)
|
||||||
|
}
|
||||||
|
defer fp.Close()
|
||||||
|
// Seek to the end to find the JSON metadata
|
||||||
|
stat, err := fp.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to stat executable file: %v", err)
|
||||||
|
}
|
||||||
|
size := stat.Size()
|
||||||
|
if size < 8 {
|
||||||
|
return nil, fmt.Errorf("executable file is too small to contain metadata")
|
||||||
|
}
|
||||||
|
_, err = fp.Seek(-8, io.SeekEnd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to seek to end of executable file: %v", err)
|
||||||
|
}
|
||||||
|
var fsSize uint64
|
||||||
|
err = binary.Read(fp, binary.LittleEndian, &fsSize)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode metadata size: %v", err)
|
||||||
|
}
|
||||||
|
fmt.Println("fsSize:", fsSize)
|
||||||
|
if fsSize == 0 || fsSize > uint64(size-8) {
|
||||||
|
return nil, fmt.Errorf("invalid metadata size: %d", fsSize)
|
||||||
|
}
|
||||||
|
_, err = fp.Seek(-(8 + int64(fsSize)), io.SeekEnd)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to seek to metadata position: %v", err)
|
||||||
|
}
|
||||||
|
jsonData := make([]byte, fsSize)
|
||||||
|
_, err = fp.Read(jsonData)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read metadata: %v", err)
|
||||||
|
}
|
||||||
|
var fs FileSystem
|
||||||
|
err = json.Unmarshal(jsonData, &fs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode JSON metadata: %v", err)
|
||||||
|
}
|
||||||
|
return &fs, nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/flate"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func compress(data []byte) []byte {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
w, _ := flate.NewWriter(&buf, flate.BestCompression)
|
||||||
|
w.Write(data)
|
||||||
|
w.Close()
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
func compress(data []byte) []byte {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
w, _ := flate.NewWriter(&buf, flate.BestCompression)
|
||||||
|
w.Write(data)
|
||||||
|
w.Close()
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
func compress(data []byte) []byte {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
w, _ := flate.NewWriter(&buf, flate.BestCompression)
|
||||||
|
w.Write(data)
|
||||||
|
w.Close()
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
func compress(data []byte) []byte {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
w, _ := flate.NewWriter(&buf, flate.BestCompression)
|
||||||
|
w.Write(data)
|
||||||
|
w.Close()
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
func compress(data []byte) []byte {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
w, _ := flate.NewWriter(&buf, flate.BestCompression)
|
||||||
|
w.Write(data)
|
||||||
|
w.Close()
|
||||||
|
return buf.Bytes()
|
||||||
|
}func compress(data []byte) []byte {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
w, _ := flate.NewWriter(&buf, flate.BestCompression)
|
||||||
|
w.Write(data)
|
||||||
|
w.Close()
|
||||||
|
return buf.Bytes()
|
||||||
|
}func compress(data []byte) []byte {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
w, _ := flate.NewWriter(&buf, flate.BestCompression)
|
||||||
|
w.Write(data)
|
||||||
|
w.Close()
|
||||||
|
return buf.Bytes()
|
||||||
|
}func compress(data []byte) []byte {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
w, _ := flate.NewWriter(&buf, flate.BestCompression)
|
||||||
|
w.Write(data)
|
||||||
|
w.Close()
|
||||||
|
return buf.Bytes()
|
||||||
|
}func compress(data []byte) []byte {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
w, _ := flate.NewWriter(&buf, flate.BestCompression)
|
||||||
|
w.Write(data)
|
||||||
|
w.Close()
|
||||||
|
return buf.Bytes()
|
||||||
|
}func compress(data []byte) []byte {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
w, _ := flate.NewWriter(&buf, flate.BestCompression)
|
||||||
|
w.Write(data)
|
||||||
|
w.Close()
|
||||||
|
return buf.Bytes()
|
||||||
|
}func compress(data []byte) []byte {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
w, _ := flate.NewWriter(&buf, flate.BestCompression)
|
||||||
|
w.Write(data)
|
||||||
|
w.Close()
|
||||||
|
return buf.Bytes()
|
||||||
|
}func compress(data []byte) []byte {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
w, _ := flate.NewWriter(&buf, flate.BestCompression)
|
||||||
|
w.Write(data)
|
||||||
|
w.Close()
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
data := []byte("1234567890abcdefghij") // 20 字节随机内容
|
||||||
|
compressed := compress(data)
|
||||||
|
|
||||||
|
fmt.Printf("原始: %d 字节\n", len(data))
|
||||||
|
fmt.Printf("压缩后: %d 字节\n", len(compressed))
|
||||||
|
fmt.Printf("比率: %.2f\n", float64(len(compressed))/float64(len(data)))
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"git.kingecg.top/kingecg/gopacker/data"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
fs, err := data.GetFileSystem()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer fs.Close()
|
||||||
|
files := fs.ListFiles()
|
||||||
|
fmt.Println("Files:", files)
|
||||||
|
content, length, err := fs.ReadFile("res.text")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error reading file:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf("File content (%d bytes):\n%s\n", length, string(content))
|
||||||
|
scontent, slen, serr := fs.ReadFile("compress.text")
|
||||||
|
if serr != nil {
|
||||||
|
fmt.Println("Error reading file:", serr)
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
fmt.Printf("File content (%d bytes):\n%s\n", slen, string(scontent))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
This is a text file.
|
||||||
Loading…
Reference in New Issue