Golang-Filepath.md


Golang file path

When we’re trying to load external files,

we can choose using relative path or absolute path.

but if we’re using absolute path,

we can’t use the same code in different environment.

So I prefer to use relative path.

Relative path problem

What I really want “relative” is relative to the current file.

but in fact, the relative path is relative to the current working directory.

So we have to constrain the folder when calling the code,

such as root of the project.

Load static file

If we want to load static file,

which won’t change in different environment,

we can use getting absolute path of the current file.

then if we’re run with go run,

or combined binary file,

we can always find the target file,

since the target file is also compiled into the binary file.

getPackageDir getting the absolute path of the current file,

if won’t change no matter what folder we’re in when calling the code.

Example code

package govel_migration

import (
	"fmt"
	"os"
	"path"
	"runtime"
	"strings"
	"time"
)

func Make(fileName string) {
	cwd, err := os.Getwd()

	if err != nil {
		panic(err)
	}

	migrationPath := path.Join(cwd, "migrations")

	if err := os.MkdirAll(migrationPath, os.ModePerm); err != nil {
		panic(err)
	}

	location, _ := time.LoadLocation("Asia/Taipei")
	prefix := time.Now().In(location).Format("2006_01_02_150405")

	filePath := path.Join(migrationPath, prefix+"_"+fileName+".go")
	file, err := os.Create(filePath)

	if err != nil {
		fmt.Println("migration file generate fail: " + filePath)
		return
	}

	content := loadStub(path.Join(getPackageDir(), "migration.stub"))
	content = strings.Replace(content, "{UpFunctionName}", "Up"+toCamelCase(fileName), -1)
	content = strings.Replace(content, "{DownFunctionName}", "Down"+toCamelCase(fileName), -1)

	file.WriteString(content)

	fmt.Println(successMessage(filePath + " created"))
}

func loadStub(fileName string) string {
	content, _ := os.ReadFile(fileName)

	return string(content)
}

func getPackageDir() string {
	_, file, _, ok := runtime.Caller(0)

	if !ok {
		panic("failed to get file path")
	}

	return path.Dir(file)
}

Load dynamic file

After many ways trying, the final solution is using relative path and limit the code-calling folder.

func loadEnv() {
	wd, _ := os.Getwd()

	envFile, _ := filepath.Abs(filepath.Join(wd, ".env"))

	if err := godotenv.Load(envFile); err != nil {
		panic(err)
	}
}

Let’s think this way,

when we run composer create-project laravel/laravel,

the file create in the current folder,

so think go run or binary file as an application,

it read the related path from the current folder.