Golang文件夹监控文件的高效安全原地修改实现问询
需求与问题
我编写了一个Go程序,用于监控指定文件夹并检测新文件,对符合条件的文件进行内容修改。当前实现存在两个核心问题:
- 效率低下:需将整个文件内容读入内存后修改
- 数据安全风险:删除原文件后再写入新文件,过程中若出错会导致数据丢失
我希望实现真正的原地直接修改(仅修改小部分内容),虽已参考资料改进代码,但未实现类似Seek的原生原地编辑逻辑,特寻求Go语言的正确实现方案。
正确的原地修改实现方案
要实现安全高效的原地文件编辑,核心是定位目标内容位置后直接修改原文件,无需删除重建。以下是针对你的需求的具体实现:
package main import ( "bytes" "fmt" "io" "os" "strings" "time" ) const dir = "dir00003" func main() { fmt.Println("Running...") for { time.Sleep(60 * time.Second) files, err := os.ReadDir(dir) if err != nil { fmt.Println("Failed to read transfer folder. There must be a folder named `dir00003`!") continue } for _, v := range files { if strings.Contains(v.Name(), "pmi") { continue } if time.Now().Sub(v.ModTime()) < (time.Minute * 2) { modifyFileInPlace(fmt.Sprintf("%s/%s", dir, v.Name())) } } fmt.Printf("\nDone with round\n") } } func modifyFileInPlace(filePath string) { // 以读写模式打开文件 f, err := os.OpenFile(filePath, os.O_RDWR, 0644) if err != nil { fmt.Printf("Couldn't open file: %s\n", filePath) return } defer f.Close() // 读取文件内容(大文件建议改用流式查找,避免内存占用过高) content, err := io.ReadAll(f) if err != nil { fmt.Printf("Couldn't read file: %s\n", filePath) return } target := []byte("<program_parameters/>") pos := bytes.Index(content, target) if pos == -1 { return // 未找到目标内容,直接跳过 } // 构造修改后的内容:移除目标字符串 newContent := append(content[:pos], content[pos+len(target):]...) // 定位到文件开头,写入新内容 if _, err := f.Seek(0, io.SeekStart); err != nil { fmt.Printf("Seek failed: %s\n", filePath) return } if _, err := f.Write(newContent); err != nil { fmt.Printf("Write failed: %s\n", filePath) return } // 截断文件到新内容长度,清除原文件末尾的残留数据 if err := f.Truncate(int64(len(newContent))); err != nil { fmt.Printf("Truncate failed: %s\n", filePath) return } fmt.Printf("Successfully modified file in-place: %s\n", filePath) }
方案优势
- 数据安全:直接在原文件上修改,无需删除重建,即使中途出错,原文件仅会出现部分修改,不会完全丢失
- 效率提升:对于大文件可优化为流式查找目标位置,无需将整个文件加载到内存
- 原生原地编辑:利用
Seek定位、Write覆盖、Truncate截断的组合,实现真正的原地修改逻辑
原实现代码
package main import ( "bytes" "fmt" "io/ioutil" "os" "strings" "time" ) const dir = "dir00003" func main() { fmt.Println("Running...") //Go into a loop forever for { //Wait 60 seconds before taking any action. time.Sleep(60 * time.Second) //Read all of the file data for all files in the directory: files, err := ioutil.ReadDir(dir) if err != nil { fmt.Println("Failed to read transfer folder. There must be a folder named `dir00003`!") continue } for _, v := range files { //if this is an index file, skip over it as we don't care: if strings.Contains(v.Name(), "pmi") { continue } //if the file was created within the last 2 minutes, we should check if we need to modify it if time.Now().Sub(v.ModTime()) < (time.Minute * 2) { //open the file f, err := os.Open(fmt.Sprintf("%s/%s", dir, v.Name())) if err != nil { fmt.Printf("\tCouldn't open file: %s\n", v.Name()) continue } defer f.Close() //read all of the bytes of the file bs, err := ioutil.ReadAll(f) if err != nil { fmt.Printf("\tCouldn't read bytes from %s\n", v.Name()) continue } //see if the <program_parameters/> tag is in the file b := bytes.Contains(bs, []byte("<program_parameters/>")) //if the tag is in the file, we should replace it, otherwise we move on to the next file if b { //replace the tag with nothing. Only look for the first instance and then abort the process of replacing. rbs := bytes.Replace(bs, []byte("<program_parameters/>"), []byte(""), 1) //close the file so we can delete it. f.Close() //delete the exisint file. os.Remove(fmt.Sprintf("%s/%s", dir, v.Name())) //create a new file with the same original name: nf, err := os.Create(fmt.Sprintf("%s/%s", dir, v.Name())) if err != nil { fmt.Printf("\tFailed to create new file for %s\n", v.Name()) continue } //write all of the bytes that we have in memory to our new file. _, err = nf.Write(rbs) if err != nil { fmt.Println("Failed to write to new file %s\n", v.Name()) continue } //close our new file nf.Close() fmt.Printf("Modified new file: %s", v.Name()) } else { continue } } } fmt.Printf("\nDone with round\n") } fmt.Println("PROGRAM STOPPED RUNNING!!") return }
改进后的代码片段
// Open the file f, err := os.OpenFile(fmt.Sprintf("%s/%s", dir, v.Name()), os.O_RDWR, 0644) if err != nil { fmt.Printf("Couldn't open file: %s\n", v.Name()) continue } defer f.Close() // Create a buffer to read the contents of the file var buffer bytes.Buffer _, err = buffer.ReadFrom(f) if err != nil { fmt.Printf("Couldn't read bytes from %s\n", v.Name()) continue } if strings.Contains(buffer.String(), "<program_parameters/>") { rbs := bytes.Replace(buffer.Bytes(), []byte("<program_parameters/>"), []byte(""), 1) err = ioutil.WriteFile(fmt.Sprintf("%s/%s",dir, v.Name()), rbs, 0660) if err != nil {fmt.Printf("Failed to WriteFile: %v", err.Error()); continue} fmt.Printf("Modified file in place: %s\n", v.Name()) }
内容的提问来源于stack exchange,提问作者Jadefox10200
相关产品推荐
相关产品推荐

