Golang中如何解决对权限为600的目录写入问题
Golang中如何解决对权限为600的目录写入问题 我正在尝试将应用部署到 Heroku,它会在 /tmp 目录中临时存储上传的文件。
ioutil.TempDir 可以在 /tmp 目录内正常创建目录。因为是 Heroku,权限被设置为 0600,但所有者与运行应用的用户相同。
问题出现在我调用 os.Create 时,最终会抛出权限错误:
2019-12-12T12:09:21.681565+00:00 app[web.1]: 2019/12/12 12:09:21 open /tmp/stream-hot146966131/stream/1080p_001.ts: permission denied
2019-12-12T12:09:21.681571+00:00 app[web.1]: 2019/12/12 12:09:21 drw-------
我添加了日志记录来在日志中显示错误,并显示 /tmp/stream-hot146966131/stream 的权限,正如你所见,它们设置是正确的。
如果 stream 目录不存在,则会创建它(该名称可能不同,它基于请求的 URL)
当然,当我在我的系统(Windows)上运行时,一切正常。
代码:
var tempDir string
func streamHandler(w http.ResponseWriter, r *http.Request) {
var rex = regexp.MustCompile("^\\/([a-zA-Z0-9]*)\\/([a-zA-Z0-9_]*\\.(?:m3u8|ts))$")
url := rex.FindStringSubmatch(r.URL.String())
if r.Method == "PUT" {
if url == nil {
http.Error(w, "URL has to be a path to HLS fragment/playlist.", http.StatusBadRequest)
return
}
stat, err := os.Stat(filepath.Join(tempDir, url[1]))
if os.IsNotExist(err) {
os.MkdirAll(filepath.Join(tempDir, url[1]), 0666)
}
f, err := os.Create(filepath.Join(tempDir, url[1], url[2]))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
log.Println(err.Error())
if stat != nil {
log.Println(stat.Mode())
}
return
}
defer f.Close()
_, err = io.Copy(f, r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.WriteHeader(200)
w.Write([]byte("OK"))
return
}
}
func registerHandlers() {
var err error
tempDir, err = ioutil.TempDir("", "stream-hot")
if err != nil {
panic(err)
}
http.HandleFunc("/", streamHandler)
}
更多关于Golang中如何解决对权限为600的目录写入问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
谢谢,这个方法确实有效!
有个小问题,这是 Go 语言本身的特性,还是 Unix/Linux 系统的一个特殊之处?
目录需要执行权限才能允许“进入”,因此 0600 对于目录来说不是一个非常有用的权限设置。你至少应该使用 0700。在你的 os.MkdirAll 中使用 0777(或者更好的 0755)应该可以工作。
在Heroku环境中,/tmp目录的权限限制确实会导致写入问题。问题在于你创建的目录权限是0666,但父目录/tmp的权限是0600,这会限制子目录的实际访问权限。
以下是解决方案:
// 修改目录创建权限为0700
os.MkdirAll(filepath.Join(tempDir, url[1]), 0700)
// 或者更好的做法:在创建临时目录时就设置正确的权限
tempDir, err = ioutil.TempDir("", "stream-hot")
if err != nil {
panic(err)
}
// 确保临时目录有执行权限
os.Chmod(tempDir, 0700)
更完整的修复方案:
func streamHandler(w http.ResponseWriter, r *http.Request) {
var rex = regexp.MustCompile("^\\/([a-zA-Z0-9]*)\\/([a-zA-Z0-9_]*\\.(?:m3u8|ts))$")
url := rex.FindStringSubmatch(r.URL.String())
if r.Method == "PUT" {
if url == nil {
http.Error(w, "URL has to be a path to HLS fragment/playlist.", http.StatusBadRequest)
return
}
dirPath := filepath.Join(tempDir, url[1])
stat, err := os.Stat(dirPath)
if os.IsNotExist(err) {
// 使用0700权限创建目录
err = os.MkdirAll(dirPath, 0700)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
} else if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// 确保目录有正确的权限
if stat != nil && stat.Mode().Perm() != 0700 {
os.Chmod(dirPath, 0700)
}
filePath := filepath.Join(dirPath, url[2])
f, err := os.Create(filePath)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
log.Printf("Create error: %v, path: %s", err, filePath)
return
}
defer f.Close()
_, err = io.Copy(f, r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.WriteHeader(200)
w.Write([]byte("OK"))
return
}
}
func registerHandlers() {
var err error
tempDir, err = ioutil.TempDir("", "stream-hot")
if err != nil {
panic(err)
}
// 设置临时目录权限
os.Chmod(tempDir, 0700)
http.HandleFunc("/", streamHandler)
}
关键点:
- 目录需要执行权限(x)才能在其中创建文件
0700权限允许所有者读、写、执行- Heroku的
/tmp目录权限限制需要子目录有足够的权限
另一种替代方案是使用os.MkdirTemp(Go 1.16+):
tempDir, err = os.MkdirTemp("", "stream-hot")
if err != nil {
panic(err)
}
// 新创建的目录默认权限就是0700

