【需求】 读取压缩包里的指定文件内容,并输出
【解决方法】 遍历循环 -> 内存地址读取
常见方法-循环遍历
/*
* @Description: 遍历-从压缩包中,提取指定文件的内容,效率较低,易出现内存泄露问题
* @param archivePath 压缩包路径
* @param fileName 文件路径 例如www/wwwxxx/11.php
* @return []byte
* @return error
*/
func extractFileFromArchive(archivePath, fileName string) ([]byte, error) {
// 打开压缩文件
archive, err := os.Open(archivePath)
if err != nil {
return nil, err
}
defer archive.Close()
// gzip读取
gzipReader, err := gzip.NewReader(archive)
if err != nil {
return nil, err
}
defer gzipReader.Close()
// tar读取
tarReader := tar.NewReader(gzipReader)
// 修复一些读取文件内容错乱
for {
header, err := tarReader.Next()
if err == io.EOF {
break
}
if err != nil {
return nil, err
}
if header.Name == filenameStr && (header.Typeflag == tar.TypeReg || header.Typeflag == tar.TypeRegA) {
// 找到目标文件,读取内容
fileContent, err := ioutil.ReadAll(tarReader)
if err != nil {
return nil, err
}
fmt.Printf("===读取到文件【%s】的内容【%s】\n", fileContent)
return fileContent, nil
}
}
// 未找到目标文件
return nil, errors.New("文件不存在")
}
这种会出现一个问题,如果站点文件极多的情况下,内存会溢出,而且扫描速度很慢,都是秒级的,效率太慢了。
【改进方法】采用map存储压缩包的文件内存地址,需要查询的时候,就直接定位文件内存地址即可读取相关内容
这种方法的核心逻辑如下:
- 打开压缩包之后。把所有的内存地址丢到一个map中,只需要遍历一次,然后直接调用
var (
FileOPen = make(map[string]*zip.File, 0)
)
func BuildIndex(zipFilename string) error {
// Open the zip file
r, err := zip.OpenReader(zipFilename)
if err != nil {
return err
}
for _, f := range r.File {
FileOPen[f.Name] = f
}
return nil
}
func main() {
zipFilename := "/www/wwwroot/222.zip"
err := BuildIndex(zipFilename)
if err != nil {
fmt.Println("Error building index:", err)
panic(err)
}
open, _ := FileOPen["xxx.com/static/app/yyy/aa/src/iiii/xx.js"].Open()
// 读取文件内容
rc, err := open.Open()
if err != nil {
return nil, err
}
data, err := ioutil.ReadAll(rc)
if err != nil {
return
}
fmt.Println(string(data))
}
【最终效果】 读取文件控制到ms级,相对于循环遍历来说,效率快的太多了