mirror of
https://github.com/unti-io/go-utils.git
synced 2025-10-30 03:01:45 +08:00
v1.0.7
v1.0.7
This commit is contained in:
219
utils/file.go
219
utils/file.go
@@ -1,13 +1,22 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"github.com/spf13/cast"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// FileStruct - File 结构体
|
||||
type FileStruct struct {
|
||||
request *FileRequest
|
||||
response *FileResponse
|
||||
}
|
||||
|
||||
// FileRequest - File 请求
|
||||
type FileRequest struct {
|
||||
// 文件名
|
||||
Name string
|
||||
@@ -17,27 +26,37 @@ type FileRequest struct {
|
||||
Dir string
|
||||
// 文件后缀
|
||||
Ext string
|
||||
// 限制行数
|
||||
Limit int
|
||||
// 读取偏移量
|
||||
Page int
|
||||
|
||||
}
|
||||
|
||||
// FileStruct - File 结构体
|
||||
type FileStruct struct {
|
||||
request *FileRequest
|
||||
response *FileResponse
|
||||
}
|
||||
|
||||
// FileResponse - File 响应
|
||||
type FileResponse struct {
|
||||
Error error
|
||||
Result string
|
||||
Result any
|
||||
Text string
|
||||
Byte []byte
|
||||
Slice []any
|
||||
}
|
||||
|
||||
// File - 文件系统
|
||||
func File(request ...FileRequest) *FileStruct {
|
||||
|
||||
if len(request) == 0 {
|
||||
request = append(request, FileRequest{})
|
||||
}
|
||||
|
||||
if IsEmpty(request[0].Limit) {
|
||||
request[0].Limit = 10
|
||||
}
|
||||
|
||||
if IsEmpty(request[0].Page) {
|
||||
request[0].Page = 1
|
||||
}
|
||||
|
||||
return &FileStruct{
|
||||
request : &request[0],
|
||||
response: &FileResponse{},
|
||||
@@ -68,6 +87,18 @@ func (this *FileStruct) Ext(ext any) *FileStruct {
|
||||
return this
|
||||
}
|
||||
|
||||
// Limit 设置限制行数
|
||||
func (this *FileStruct) Limit(limit any) *FileStruct {
|
||||
this.request.Limit = cast.ToInt(limit)
|
||||
return this
|
||||
}
|
||||
|
||||
// Page 设置读取偏移量
|
||||
func (this *FileStruct) Page(page any) *FileStruct {
|
||||
this.request.Page = cast.ToInt(page)
|
||||
return this
|
||||
}
|
||||
|
||||
// Save 保存文件
|
||||
func (this *FileStruct) Save(reader io.Reader, path ...string) (result *FileResponse) {
|
||||
|
||||
@@ -80,9 +111,7 @@ func (this *FileStruct) Save(reader io.Reader, path ...string) (result *FileResp
|
||||
return this.response
|
||||
}
|
||||
|
||||
filePath := cast.ToString(path)
|
||||
|
||||
dir := filepath.Dir(filePath)
|
||||
dir := filepath.Dir(this.request.Path)
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
// 目录不存在,需要创建
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
@@ -92,7 +121,7 @@ func (this *FileStruct) Save(reader io.Reader, path ...string) (result *FileResp
|
||||
}
|
||||
|
||||
// 创建文件
|
||||
file, err := os.Create(filePath)
|
||||
file, err := os.Create(this.request.Path)
|
||||
if err != nil {
|
||||
this.response.Error = err
|
||||
return this.response
|
||||
@@ -114,7 +143,7 @@ func (this *FileStruct) Save(reader io.Reader, path ...string) (result *FileResp
|
||||
return this.response
|
||||
}
|
||||
|
||||
return nil
|
||||
return this.response
|
||||
}
|
||||
|
||||
// Byte 获取文件字节
|
||||
@@ -142,33 +171,51 @@ func (this *FileStruct) Byte(path ...any) (result *FileResponse) {
|
||||
}
|
||||
}(file)
|
||||
|
||||
// // 获取文件大小
|
||||
// info, _ := file.Stat()
|
||||
// size := info.Size()
|
||||
// // 读取文件
|
||||
// data := make([]byte, size)
|
||||
// file.Read(data)
|
||||
// return data
|
||||
// 获取文件信息
|
||||
info, err := file.Stat()
|
||||
if err != nil {
|
||||
this.response.Error = err
|
||||
return this.response
|
||||
}
|
||||
size := info.Size()
|
||||
|
||||
var bytes []byte
|
||||
|
||||
// 分批次读取
|
||||
buf := make([]byte, 1024)
|
||||
for {
|
||||
line, err := file.Read(buf)
|
||||
// 小于50MB,整体读取
|
||||
if size < 50 * 1024 * 1024 {
|
||||
bytes := make([]byte, size)
|
||||
_, err = file.Read(bytes)
|
||||
if err != nil {
|
||||
this.response.Error = err
|
||||
return this.response
|
||||
}
|
||||
this.response.Byte = bytes
|
||||
this.response.Text = string(bytes)
|
||||
this.response.Result = bytes
|
||||
return this.response
|
||||
}
|
||||
|
||||
// 大于等于50MB,分块读取
|
||||
var bytes []byte
|
||||
buffer := make([]byte, 1024 * 1024)
|
||||
for {
|
||||
index, err := file.Read(buffer)
|
||||
if err != nil && err != io.EOF {
|
||||
this.response.Error = err
|
||||
break
|
||||
}
|
||||
bytes = append(bytes, buffer[:index]...)
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
bytes = append(bytes, buf[:line]...)
|
||||
}
|
||||
|
||||
this.response.Byte = bytes
|
||||
this.response.Result = string(bytes)
|
||||
this.response.Text = string(bytes)
|
||||
this.response.Result = bytes
|
||||
|
||||
return this.response
|
||||
}
|
||||
|
||||
// FileList 获取指定目录下的所有文件
|
||||
// List 获取指定目录下的所有文件
|
||||
func (this *FileStruct) List(opt ...map[string]any) (result *FileResponse) {
|
||||
|
||||
// 默认参数
|
||||
@@ -243,5 +290,121 @@ func (this *FileStruct) List(opt ...map[string]any) (result *FileResponse) {
|
||||
}
|
||||
|
||||
this.response.Slice = cast.ToSlice(slice)
|
||||
return this.response
|
||||
}
|
||||
|
||||
// IsExist 判断文件是否存在
|
||||
func (this *FileStruct) IsExist(path ...any) (ok bool) {
|
||||
|
||||
if len(path) != 0 {
|
||||
this.request.Path = cast.ToString(path[0])
|
||||
}
|
||||
|
||||
if IsEmpty(this.request.Path) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 判断文件是否存在
|
||||
if _, err := os.Stat(this.request.Path); os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Line 按行读取文件
|
||||
func (this *FileStruct) Line(path ...any) (result *FileResponse) {
|
||||
|
||||
if len(path) != 0 {
|
||||
this.request.Path = cast.ToString(path[0])
|
||||
}
|
||||
|
||||
if IsEmpty(this.request.Path) {
|
||||
this.response.Error = errors.New("文件路径不能为空")
|
||||
return this.response
|
||||
}
|
||||
|
||||
// 读取块
|
||||
readBlock := func(file *os.File, start int, end int) ([]string, error) {
|
||||
|
||||
lines := make([]string, 0)
|
||||
scanner := bufio.NewScanner(file)
|
||||
|
||||
// 移动扫描器到指定的起始行
|
||||
for i := 1; i < start && scanner.Scan(); i++ {}
|
||||
|
||||
// 开始读取需要的行
|
||||
for i := start; i <= end && scanner.Scan(); i++ {
|
||||
// 只把需要的行保存到切片中
|
||||
if i >= start && i <= end {
|
||||
lines = append(lines, scanner.Text())
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return lines, nil
|
||||
}
|
||||
|
||||
file, err := os.Open(this.request.Path)
|
||||
if err != nil {
|
||||
this.response.Error = err
|
||||
return this.response
|
||||
}
|
||||
defer func(file *os.File) {
|
||||
err := file.Close()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}(file)
|
||||
|
||||
end := this.request.Page * this.request.Limit
|
||||
start := end - this.request.Limit + 1
|
||||
|
||||
lines := make([]string, 0)
|
||||
// 计算每个块的大小(以10MB为一个块)
|
||||
blockSize := 10 * 1024 * 1024
|
||||
numBlocks := (end - start + 1) / blockSize
|
||||
|
||||
if (end - start + 1) % blockSize != 0 {
|
||||
numBlocks++
|
||||
}
|
||||
|
||||
// 并发读取每个块
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(numBlocks)
|
||||
for i := 0; i < numBlocks; i++ {
|
||||
go func(i int) {
|
||||
|
||||
startLine := start + i * blockSize
|
||||
endLine := startLine + blockSize - 1
|
||||
if endLine > end {
|
||||
endLine = end
|
||||
}
|
||||
|
||||
blockLines, err := readBlock(file, startLine, endLine)
|
||||
if err != nil {
|
||||
this.response.Error = err
|
||||
return
|
||||
} else {
|
||||
lines = append(lines, blockLines...)
|
||||
}
|
||||
|
||||
wg.Done()
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
this.response.Result = lines
|
||||
this.response.Text = JsonEncode(this.response.Result)
|
||||
this.response.Byte = []byte(this.response.Text)
|
||||
|
||||
for _, v := range lines {
|
||||
this.response.Slice = append(this.response.Slice, v)
|
||||
}
|
||||
|
||||
return this.response
|
||||
}
|
||||
Reference in New Issue
Block a user