1883 lines
45 KiB
Go
1883 lines
45 KiB
Go
package main
|
||
|
||
/*
|
||
#include <stdlib.h>
|
||
*/
|
||
import "C"
|
||
import (
|
||
"bufio"
|
||
"encoding/csv"
|
||
"encoding/json"
|
||
"fmt"
|
||
"io"
|
||
"os"
|
||
"path/filepath"
|
||
"sync"
|
||
"sync/atomic"
|
||
"time"
|
||
)
|
||
|
||
// 修改操作类型
|
||
type ModifyType int
|
||
|
||
const (
|
||
ModifyRow ModifyType = iota // 修改整行
|
||
ModifyCell // 修改单元格
|
||
InsertRow // 插入行
|
||
DeleteRow // 删除行
|
||
)
|
||
|
||
// ModifyRequest 修改请求
|
||
type ModifyRequest struct {
|
||
HandleID int64 // 句柄ID
|
||
RowNumber int64 // 行号(从1开始)
|
||
ModifyType ModifyType // 修改类型
|
||
NewRow []string // 新行数据(整行修改)
|
||
NewCellValue string // 新单元格值
|
||
ColumnIndex int // 列索引(单元格修改时使用)
|
||
ColumnName string // 列名(可选,单元格修改时使用)
|
||
}
|
||
|
||
// ModifyResult 修改结果
|
||
type ModifyResult struct {
|
||
Success bool // 是否成功
|
||
Message string // 消息
|
||
RowsAffected int64 // 影响的行数
|
||
BackupFile string // 备份文件路径
|
||
OperationID string // 操作ID(用于追踪)
|
||
}
|
||
|
||
// RowPosition 行位置信息
|
||
type RowPosition struct {
|
||
StartOffset int64 // 行开始偏移
|
||
EndOffset int64 // 行结束偏移
|
||
RowLength int // 行长度(字节数)
|
||
}
|
||
|
||
// CSVHandle CSV文件句柄
|
||
type CSVHandle struct {
|
||
ID int64 // 句柄唯一ID
|
||
Filename string // 文件名
|
||
Delimiter rune // 分隔符
|
||
HasHeader bool // 是否有表头
|
||
Header []string // 表头(如果有)
|
||
File *os.File // 底层文件句柄
|
||
CSVReader *csv.Reader // CSV阅读器
|
||
TotalRows int64 // 总行数(如果已计算)
|
||
IsOpen bool // 是否已打开
|
||
OpenTime time.Time // 打开时间
|
||
AccessTime time.Time // 最后访问时间
|
||
AccessCount int64 // 访问计数
|
||
mu sync.RWMutex // 读写锁(保护数据结构)
|
||
writeMu sync.Mutex // 写入专用锁(保证写入互斥)
|
||
autoCloseTimer *time.Timer // 自动关闭计时器
|
||
|
||
// 修改相关
|
||
rowIndex []RowPosition // 行索引(用于快速定位)
|
||
indexBuilt bool // 索引是否已构建
|
||
tempDir string // 临时目录
|
||
backupFiles []string // 备份文件列表
|
||
operationLog []string // 操作日志
|
||
}
|
||
|
||
// CSVManager CSV文件管理器
|
||
type CSVManager struct {
|
||
handles sync.Map // map[int64]*CSVHandle
|
||
nextHandle int64 // 下一个句柄ID
|
||
maxOpen int // 最大打开文件数
|
||
semaphore chan struct{} // 信号量控制并发打开
|
||
config ManagerConfig // 管理器配置
|
||
fileLocks *sync.Map // 文件级锁映射
|
||
}
|
||
|
||
// ManagerConfig 管理器配置
|
||
type ManagerConfig struct {
|
||
MaxOpenFiles int // 最大打开文件数
|
||
AutoCloseTimeout time.Duration // 自动关闭超时
|
||
BufferSize int // 缓冲区大小
|
||
UseMMap bool // 是否使用内存映射(大文件)
|
||
MMapThreshold int64 // 使用内存映射的阈值(字节)
|
||
CacheRows int // 缓存行数
|
||
MaxWriteRetries int // 最大写入重试次数
|
||
WriteTimeout time.Duration // 写入超时时间
|
||
}
|
||
|
||
// DefaultConfig 默认配置
|
||
var DefaultConfig = ManagerConfig{
|
||
MaxOpenFiles: 100,
|
||
AutoCloseTimeout: 5 * time.Minute,
|
||
BufferSize: 32 * 1024, // 32KB
|
||
UseMMap: true,
|
||
MMapThreshold: 10 * 1024 * 1024, // 10MB
|
||
CacheRows: 1000,
|
||
MaxWriteRetries: 3,
|
||
WriteTimeout: 30 * time.Second,
|
||
}
|
||
|
||
// 全局管理器实例
|
||
var (
|
||
globalManager *CSVManager
|
||
managerInitOnce sync.Once
|
||
)
|
||
|
||
// GetManager 获取全局管理器
|
||
func GetManager() *CSVManager {
|
||
managerInitOnce.Do(func() {
|
||
globalManager = NewCSVManager(DefaultConfig)
|
||
})
|
||
return globalManager
|
||
}
|
||
|
||
// NewCSVManager 创建新的CSV管理器
|
||
func NewCSVManager(config ManagerConfig) *CSVManager {
|
||
if config.MaxOpenFiles <= 0 {
|
||
config.MaxOpenFiles = DefaultConfig.MaxOpenFiles
|
||
}
|
||
|
||
return &CSVManager{
|
||
handles: sync.Map{},
|
||
nextHandle: 1,
|
||
maxOpen: config.MaxOpenFiles,
|
||
semaphore: make(chan struct{}, config.MaxOpenFiles),
|
||
config: config,
|
||
fileLocks: &sync.Map{},
|
||
}
|
||
}
|
||
|
||
// getFileLock 获取文件级锁
|
||
func (mgr *CSVManager) getFileLock(filename string) *sync.RWMutex {
|
||
lock, _ := mgr.fileLocks.LoadOrStore(filename, &sync.RWMutex{})
|
||
return lock.(*sync.RWMutex)
|
||
}
|
||
|
||
// ========================== 文件操作 ==========================
|
||
|
||
// OpenCSVFile 打开CSV文件并返回句柄
|
||
func (mgr *CSVManager) OpenCSVFile(filename string, delimiter rune, hasHeader bool) (int64, error) {
|
||
// 检查文件是否存在
|
||
if _, err := os.Stat(filename); os.IsNotExist(err) {
|
||
return -1, fmt.Errorf("文件不存在: %s", filename)
|
||
}
|
||
|
||
// 限制并发打开文件数
|
||
mgr.semaphore <- struct{}{}
|
||
defer func() { <-mgr.semaphore }()
|
||
|
||
// 生成唯一句柄ID
|
||
handleID := atomic.AddInt64(&mgr.nextHandle, 1)
|
||
|
||
// 创建CSV句柄
|
||
csvHandle := &CSVHandle{
|
||
ID: handleID,
|
||
Filename: filename,
|
||
Delimiter: delimiter,
|
||
HasHeader: hasHeader,
|
||
OpenTime: time.Now(),
|
||
AccessTime: time.Now(),
|
||
}
|
||
|
||
// 打开文件
|
||
if err := mgr.openFile(csvHandle); err != nil {
|
||
return -1, fmt.Errorf("打开文件失败: %w", err)
|
||
}
|
||
|
||
// 获取总行数
|
||
rows, err := csvHandle.GetTotalRows()
|
||
if err != nil {
|
||
return -1, err
|
||
}
|
||
csvHandle.TotalRows = rows
|
||
|
||
// 读取表头(如果需要)
|
||
if hasHeader {
|
||
if err := mgr.readHeader(csvHandle); err != nil {
|
||
csvHandle.Close()
|
||
return -1, fmt.Errorf("读取表头失败: %w", err)
|
||
}
|
||
}
|
||
|
||
// 注册到管理器
|
||
mgr.handles.Store(handleID, csvHandle)
|
||
|
||
// 启动自动关闭计时器
|
||
if mgr.config.AutoCloseTimeout > 0 {
|
||
csvHandle.startAutoClose(mgr.config.AutoCloseTimeout, mgr)
|
||
}
|
||
|
||
return handleID, nil
|
||
}
|
||
|
||
// openFile 打开文件
|
||
func (mgr *CSVManager) openFile(handle *CSVHandle) error {
|
||
handle.mu.Lock()
|
||
defer handle.mu.Unlock()
|
||
|
||
if handle.IsOpen {
|
||
return nil
|
||
}
|
||
|
||
// 获取文件大小
|
||
fileInfo, err := os.Stat(handle.Filename)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
fileSize := fileInfo.Size()
|
||
|
||
// 根据文件大小选择打开策略
|
||
if mgr.config.UseMMap && fileSize > mgr.config.MMapThreshold {
|
||
return mgr.openFileWithMMap(handle, fileSize)
|
||
}
|
||
|
||
return mgr.openFileNormal(handle)
|
||
}
|
||
|
||
// openFileNormal 正常打开文件
|
||
func (mgr *CSVManager) openFileNormal(handle *CSVHandle) error {
|
||
file, err := os.Open(handle.Filename)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// 创建带缓冲的CSV阅读器
|
||
reader := csv.NewReader(bufio.NewReaderSize(file, mgr.config.BufferSize))
|
||
reader.Comma = handle.Delimiter
|
||
reader.LazyQuotes = true
|
||
reader.TrimLeadingSpace = true
|
||
|
||
handle.File = file
|
||
handle.CSVReader = reader
|
||
handle.IsOpen = true
|
||
handle.AccessTime = time.Now()
|
||
return nil
|
||
}
|
||
|
||
// openFileWithMMap 使用内存映射打开大文件
|
||
func (mgr *CSVManager) openFileWithMMap(handle *CSVHandle, fileSize int64) error {
|
||
file, err := os.Open(handle.Filename)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// 对于大文件,我们可以先只打开,按需读取
|
||
reader := csv.NewReader(file)
|
||
reader.Comma = handle.Delimiter
|
||
reader.LazyQuotes = true
|
||
|
||
handle.File = file
|
||
handle.CSVReader = reader
|
||
handle.IsOpen = true
|
||
handle.AccessTime = time.Now()
|
||
|
||
return nil
|
||
}
|
||
|
||
// readHeader 读取CSV表头
|
||
func (mgr *CSVManager) readHeader(handle *CSVHandle) error {
|
||
handle.mu.Lock()
|
||
defer handle.mu.Unlock()
|
||
|
||
if !handle.IsOpen {
|
||
return fmt.Errorf("文件未打开")
|
||
}
|
||
|
||
// 确保文件指针在开头
|
||
if _, err := handle.File.Seek(0, 0); err != nil {
|
||
return err
|
||
}
|
||
|
||
// 重置CSV阅读器
|
||
handle.CSVReader = csv.NewReader(handle.File)
|
||
handle.CSVReader.Comma = handle.Delimiter
|
||
|
||
// 读取表头
|
||
header, err := handle.CSVReader.Read()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
handle.Header = header
|
||
return nil
|
||
}
|
||
|
||
// GetHandle 获取句柄对象
|
||
func (mgr *CSVManager) GetHandle(handleID int64) (*CSVHandle, error) {
|
||
value, ok := mgr.handles.Load(handleID)
|
||
if !ok {
|
||
return nil, fmt.Errorf("句柄不存在: %d", handleID)
|
||
}
|
||
|
||
handle := value.(*CSVHandle)
|
||
|
||
// 确保文件已打开
|
||
handle.mu.RLock()
|
||
isOpen := handle.IsOpen
|
||
handle.mu.RUnlock()
|
||
|
||
if !isOpen {
|
||
if err := mgr.openFile(handle); err != nil {
|
||
return nil, fmt.Errorf("重新打开文件失败: %w", err)
|
||
}
|
||
}
|
||
|
||
// 更新访问时间
|
||
atomic.AddInt64(&handle.AccessCount, 1)
|
||
handle.AccessTime = time.Now()
|
||
|
||
return handle, nil
|
||
}
|
||
|
||
// ========================== 读取操作 ==========================
|
||
|
||
// ReadRow 读取一行数据
|
||
func (h *CSVHandle) ReadRow() ([]string, error) {
|
||
h.mu.RLock()
|
||
defer h.mu.RUnlock()
|
||
|
||
if !h.IsOpen {
|
||
return nil, fmt.Errorf("文件未打开")
|
||
}
|
||
|
||
if h.CSVReader == nil {
|
||
return nil, fmt.Errorf("CSV阅读器未初始化")
|
||
}
|
||
|
||
return h.CSVReader.Read()
|
||
}
|
||
|
||
// ReadAllRows 读取所有行
|
||
func (h *CSVHandle) ReadAllRows() ([][]string, error) {
|
||
h.mu.Lock()
|
||
defer h.mu.Unlock()
|
||
|
||
if !h.IsOpen {
|
||
return nil, fmt.Errorf("文件未打开")
|
||
}
|
||
|
||
// 重置文件指针到数据开始位置
|
||
if _, err := h.File.Seek(0, 0); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 重新创建CSV阅读器
|
||
h.CSVReader = csv.NewReader(h.File)
|
||
h.CSVReader.Comma = h.Delimiter
|
||
|
||
// 跳过表头
|
||
if h.HasHeader {
|
||
if _, err := h.CSVReader.Read(); err != nil {
|
||
return nil, err
|
||
}
|
||
}
|
||
|
||
// 读取所有行
|
||
return h.CSVReader.ReadAll()
|
||
}
|
||
|
||
// ReadRows 读取指定数量的行
|
||
func (h *CSVHandle) ReadRows(count int) ([][]string, error) {
|
||
h.mu.Lock()
|
||
defer h.mu.Unlock()
|
||
|
||
if !h.IsOpen {
|
||
return nil, fmt.Errorf("文件未打开")
|
||
}
|
||
|
||
rows := make([][]string, 0, count)
|
||
|
||
for i := 0; i < count; i++ {
|
||
row, err := h.CSVReader.Read()
|
||
if err == io.EOF {
|
||
break
|
||
}
|
||
if err != nil {
|
||
return rows, err
|
||
}
|
||
rows = append(rows, row)
|
||
}
|
||
|
||
return rows, nil
|
||
}
|
||
|
||
// GetTotalRows 获取总行数
|
||
func (h *CSVHandle) GetTotalRows() (int64, error) {
|
||
h.mu.Lock()
|
||
defer h.mu.Unlock()
|
||
|
||
if h.TotalRows > 0 {
|
||
return h.TotalRows, nil
|
||
}
|
||
|
||
// 保存当前文件位置
|
||
currentPos, err := h.File.Seek(0, io.SeekCurrent)
|
||
if err != nil {
|
||
return 0, err
|
||
}
|
||
defer h.File.Seek(currentPos, io.SeekStart)
|
||
|
||
// 重置到文件开始
|
||
if _, err := h.File.Seek(0, 0); err != nil {
|
||
return 0, err
|
||
}
|
||
|
||
// 创建新的CSV阅读器进行计数
|
||
reader := csv.NewReader(h.File)
|
||
reader.Comma = h.Delimiter
|
||
|
||
var count int64 = 0
|
||
|
||
// 跳过表头
|
||
if h.HasHeader {
|
||
if _, err := reader.Read(); err != nil {
|
||
return 0, err
|
||
}
|
||
}
|
||
|
||
// 计数行数
|
||
for {
|
||
_, err := reader.Read()
|
||
if err == io.EOF {
|
||
break
|
||
}
|
||
if err != nil {
|
||
return count, err
|
||
}
|
||
count++
|
||
}
|
||
|
||
h.TotalRows = count
|
||
return count, nil
|
||
}
|
||
|
||
// ========================== 修改操作 ==========================
|
||
|
||
// ModifyRow 修改指定行
|
||
func (mgr *CSVManager) ModifyRow(handleID int64, rowNumber int64, newRow []string) (*ModifyResult, error) {
|
||
req := &ModifyRequest{
|
||
HandleID: handleID,
|
||
RowNumber: rowNumber,
|
||
ModifyType: ModifyRow,
|
||
NewRow: newRow,
|
||
}
|
||
|
||
return mgr.ModifyCSV(req)
|
||
}
|
||
|
||
// ModifyCell 修改指定单元格
|
||
func (mgr *CSVManager) ModifyCell(handleID int64, rowNumber int64, columnIndex int, newValue string) (*ModifyResult, error) {
|
||
req := &ModifyRequest{
|
||
HandleID: handleID,
|
||
RowNumber: rowNumber,
|
||
ModifyType: ModifyCell,
|
||
ColumnIndex: columnIndex,
|
||
NewCellValue: newValue,
|
||
}
|
||
|
||
return mgr.ModifyCSV(req)
|
||
}
|
||
|
||
// ModifyCSV 通用的CSV修改函数
|
||
func (mgr *CSVManager) ModifyCSV(req *ModifyRequest) (*ModifyResult, error) {
|
||
// 1. 获取句柄
|
||
handle, err := mgr.GetHandle(req.HandleID)
|
||
if err != nil {
|
||
return &ModifyResult{
|
||
Success: false,
|
||
Message: fmt.Sprintf("获取句柄失败: %v", err),
|
||
}, err
|
||
}
|
||
|
||
// 2. 生成操作ID
|
||
operationID := fmt.Sprintf("%d_%d_%d", req.HandleID, time.Now().UnixNano(), req.RowNumber)
|
||
|
||
// 3. 获取文件级写锁
|
||
fileLock := mgr.getFileLock(handle.Filename)
|
||
fileLock.Lock()
|
||
defer fileLock.Unlock()
|
||
|
||
// 4. 验证基本参数
|
||
if err := mgr.validateModifyRequestBeforeLock(handle, req); err != nil {
|
||
return &ModifyResult{
|
||
Success: false,
|
||
Message: fmt.Sprintf("参数验证失败: %v", err),
|
||
}, err
|
||
}
|
||
|
||
// 5. 获取句柄写入锁
|
||
handle.writeMu.Lock()
|
||
defer handle.writeMu.Unlock()
|
||
|
||
// 6. 执行修改操作
|
||
result, err := mgr.executeModifyWithRetry(handle, req, operationID)
|
||
if result != nil {
|
||
result.OperationID = operationID
|
||
}
|
||
|
||
return result, err
|
||
}
|
||
|
||
// validateModifyRequestBeforeLock 验证修改请求(无需句柄锁)
|
||
func (mgr *CSVManager) validateModifyRequestBeforeLock(handle *CSVHandle, req *ModifyRequest) error {
|
||
if req.RowNumber <= 0 {
|
||
return fmt.Errorf("行号必须大于0")
|
||
}
|
||
|
||
// 验证列数
|
||
handle.mu.RLock()
|
||
header := handle.Header
|
||
handle.mu.RUnlock()
|
||
|
||
if req.ModifyType == ModifyRow {
|
||
expectedCols := len(header)
|
||
if len(req.NewRow) != expectedCols {
|
||
return fmt.Errorf("新行数据列数不匹配,期望: %d,实际: %d", expectedCols, len(req.NewRow))
|
||
}
|
||
}
|
||
|
||
if req.ModifyType == ModifyCell {
|
||
expectedCols := len(header)
|
||
if req.ColumnIndex < 0 || req.ColumnIndex >= expectedCols {
|
||
return fmt.Errorf("列索引超出范围,有效范围: 0-%d", expectedCols-1)
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// executeModifyWithRetry 带重试的修改执行
|
||
func (mgr *CSVManager) executeModifyWithRetry(handle *CSVHandle, req *ModifyRequest, operationID string) (*ModifyResult, error) {
|
||
var lastErr error
|
||
var result *ModifyResult
|
||
|
||
for retry := 0; retry <= mgr.config.MaxWriteRetries; retry++ {
|
||
if retry > 0 {
|
||
// 重试前等待
|
||
time.Sleep(time.Duration(retry*100) * time.Millisecond)
|
||
}
|
||
|
||
result, lastErr = mgr.executeModifyOnce(handle, req, operationID)
|
||
if lastErr == nil {
|
||
// 记录操作日志
|
||
handle.mu.Lock()
|
||
handle.operationLog = append(handle.operationLog,
|
||
fmt.Sprintf("%s: %s", operationID, result.Message))
|
||
handle.mu.Unlock()
|
||
return result, nil
|
||
}
|
||
}
|
||
|
||
return &ModifyResult{
|
||
Success: false,
|
||
Message: fmt.Sprintf("修改失败,重试%d次后仍失败: %v", mgr.config.MaxWriteRetries, lastErr),
|
||
OperationID: operationID,
|
||
}, lastErr
|
||
}
|
||
|
||
// executeModifyOnce 执行单次修改
|
||
func (mgr *CSVManager) executeModifyOnce(handle *CSVHandle, req *ModifyRequest, operationID string) (*ModifyResult, error) {
|
||
// 1. 创建备份
|
||
backupFile, err := mgr.createBackup(handle)
|
||
if err != nil {
|
||
return &ModifyResult{
|
||
Success: false,
|
||
Message: fmt.Sprintf("创建备份失败: %v", err),
|
||
}, err
|
||
}
|
||
|
||
// 2. 获取总行数(使用更高效的方式)
|
||
totalRows, err := mgr.getTotalRowsForModification(handle)
|
||
if err != nil {
|
||
return &ModifyResult{
|
||
Success: false,
|
||
Message: fmt.Sprintf("获取总行数失败: %v", err),
|
||
}, err
|
||
}
|
||
|
||
//// 2. 获取总行数用于验证
|
||
//totalRows, err := mgr.calculateTotalRowsWithLock(handle)
|
||
//if err != nil {
|
||
// return &ModifyResult{
|
||
// Success: false,
|
||
// Message: fmt.Sprintf("获取总行数失败: %v", err),
|
||
// }, err
|
||
//}
|
||
|
||
// 3. 验证行号范围
|
||
if err := mgr.validateRowNumber(handle, req, totalRows); err != nil {
|
||
return &ModifyResult{
|
||
Success: false,
|
||
Message: fmt.Sprintf("行号验证失败: %v", err),
|
||
}, err
|
||
}
|
||
|
||
// 4. 执行修改逻辑
|
||
startTime := time.Now()
|
||
var result *ModifyResult
|
||
var modifyErr error
|
||
|
||
switch req.ModifyType {
|
||
case ModifyRow:
|
||
result, modifyErr = mgr.modifyRowInternal(handle, req, totalRows)
|
||
case ModifyCell:
|
||
result, modifyErr = mgr.modifyCellInternal(handle, req, totalRows)
|
||
case InsertRow:
|
||
result, modifyErr = mgr.insertRowInternal(handle, req, totalRows)
|
||
case DeleteRow:
|
||
result, modifyErr = mgr.deleteRowInternal(handle, req, totalRows)
|
||
default:
|
||
modifyErr = fmt.Errorf("不支持的修改类型: %v", req.ModifyType)
|
||
result = &ModifyResult{
|
||
Success: false,
|
||
Message: fmt.Sprintf("不支持的修改类型: %v", req.ModifyType),
|
||
}
|
||
}
|
||
|
||
// 记录执行时间
|
||
elapsed := time.Since(startTime)
|
||
if elapsed > 1*time.Second {
|
||
fmt.Printf("警告:修改操作耗时较长: %v\n", elapsed)
|
||
}
|
||
// 5. 设置备份文件路径
|
||
if result != nil {
|
||
result.BackupFile = backupFile
|
||
result.OperationID = operationID
|
||
}
|
||
|
||
// 6. 记录备份文件
|
||
if backupFile != "" {
|
||
handle.mu.Lock()
|
||
handle.backupFiles = append(handle.backupFiles, backupFile)
|
||
handle.mu.Unlock()
|
||
}
|
||
|
||
return result, modifyErr
|
||
}
|
||
|
||
// getTotalRowsForModification 为修改操作获取总行数(优化版本)
|
||
func (mgr *CSVManager) getTotalRowsForModification(handle *CSVHandle) (int64, error) {
|
||
// 先检查是否已有缓存
|
||
if handle.TotalRows > 0 {
|
||
return handle.TotalRows, nil
|
||
}
|
||
|
||
// 如果文件不大,直接快速计算
|
||
fileInfo, err := os.Stat(handle.Filename)
|
||
if err != nil {
|
||
return 0, err
|
||
}
|
||
|
||
// 对于小文件,使用快速计算方法
|
||
if fileInfo.Size() < 10*1024*1024 { // 10MB以下
|
||
return mgr.fastCountRows(handle)
|
||
}
|
||
|
||
// 大文件使用原来的方法
|
||
return mgr.calculateTotalRowsWithLock(handle)
|
||
}
|
||
|
||
// fastCountRows 快速计算行数(无需复杂锁)
|
||
func (mgr *CSVManager) fastCountRows(handle *CSVHandle) (int64, error) {
|
||
file, err := os.Open(handle.Filename)
|
||
if err != nil {
|
||
return 0, err
|
||
}
|
||
defer file.Close()
|
||
|
||
reader := csv.NewReader(file)
|
||
reader.Comma = handle.Delimiter
|
||
|
||
var count int64 = 0
|
||
for {
|
||
_, err := reader.Read()
|
||
if err == io.EOF {
|
||
break
|
||
}
|
||
if err != nil {
|
||
return count, err
|
||
}
|
||
count++
|
||
}
|
||
|
||
// 减去表头行
|
||
if handle.HasHeader {
|
||
count--
|
||
}
|
||
|
||
// 缓存结果
|
||
handle.mu.Lock()
|
||
handle.TotalRows = count
|
||
handle.mu.Unlock()
|
||
|
||
return count, nil
|
||
}
|
||
|
||
// calculateTotalRowsWithLock 计算总行数(带锁)
|
||
func (mgr *CSVManager) calculateTotalRowsWithLock(handle *CSVHandle) (int64, error) {
|
||
handle.mu.Lock()
|
||
defer handle.mu.Unlock()
|
||
|
||
if handle.TotalRows > 0 {
|
||
return handle.TotalRows, nil
|
||
}
|
||
|
||
// 保存当前文件位置
|
||
currentPos, err := handle.File.Seek(0, io.SeekCurrent)
|
||
if err != nil {
|
||
return 0, err
|
||
}
|
||
defer handle.File.Seek(currentPos, io.SeekStart)
|
||
|
||
// 重置到文件开始
|
||
if _, err := handle.File.Seek(0, 0); err != nil {
|
||
return 0, err
|
||
}
|
||
|
||
// 创建新的CSV阅读器进行计数
|
||
reader := csv.NewReader(handle.File)
|
||
reader.Comma = handle.Delimiter
|
||
|
||
var count int64 = 0
|
||
|
||
// 跳过表头
|
||
if handle.HasHeader {
|
||
if _, err := reader.Read(); err != nil {
|
||
return 0, err
|
||
}
|
||
}
|
||
|
||
// 计数行数
|
||
for {
|
||
_, err := reader.Read()
|
||
if err == io.EOF {
|
||
break
|
||
}
|
||
if err != nil {
|
||
return count, err
|
||
}
|
||
count++
|
||
}
|
||
|
||
handle.TotalRows = count
|
||
return count, nil
|
||
}
|
||
|
||
// validateRowNumber 验证行号
|
||
func (mgr *CSVManager) validateRowNumber(handle *CSVHandle, req *ModifyRequest, totalRows int64) error {
|
||
// 调整行号(考虑表头)
|
||
adjustedRowNumber := req.RowNumber
|
||
if handle.HasHeader {
|
||
adjustedRowNumber = req.RowNumber + 1 // 表头占第1行
|
||
}
|
||
|
||
// 验证行号范围
|
||
if req.ModifyType == ModifyRow || req.ModifyType == ModifyCell {
|
||
if adjustedRowNumber > totalRows {
|
||
return fmt.Errorf("行号超出范围,最大行数为: %d", totalRows-1)
|
||
}
|
||
} else if req.ModifyType == InsertRow {
|
||
// 插入行可以在末尾
|
||
if adjustedRowNumber > totalRows+1 {
|
||
return fmt.Errorf("插入行号超出范围,最大行数为: %d", totalRows)
|
||
}
|
||
} else if req.ModifyType == DeleteRow {
|
||
if adjustedRowNumber > totalRows {
|
||
return fmt.Errorf("删除行号超出范围,最大行数为: %d", totalRows-1)
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// createBackup 创建备份文件
|
||
func (mgr *CSVManager) createBackup(handle *CSVHandle) (string, error) {
|
||
handle.mu.RLock()
|
||
defer handle.mu.RUnlock()
|
||
|
||
// 创建临时目录
|
||
if handle.tempDir == "" {
|
||
tempDir, err := os.MkdirTemp("", fmt.Sprintf("csv_backup_%d_", handle.ID))
|
||
if err != nil {
|
||
return "", fmt.Errorf("创建临时目录失败: %v", err)
|
||
}
|
||
handle.tempDir = tempDir
|
||
}
|
||
|
||
// 生成备份文件名
|
||
backupFile := filepath.Join(handle.tempDir,
|
||
fmt.Sprintf("backup_%d_%s.csv", time.Now().UnixNano(),
|
||
filepath.Base(handle.Filename)))
|
||
|
||
// 复制文件
|
||
src, err := os.Open(handle.Filename)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
defer src.Close()
|
||
|
||
dst, err := os.Create(backupFile)
|
||
if err != nil {
|
||
return "", err
|
||
}
|
||
defer dst.Close()
|
||
|
||
_, err = io.Copy(dst, src)
|
||
if err != nil {
|
||
os.Remove(backupFile) // 删除失败的备份文件
|
||
return "", err
|
||
}
|
||
|
||
return backupFile, nil
|
||
}
|
||
|
||
// modifyRowInternal 修改整行
|
||
func (mgr *CSVManager) modifyRowInternal(handle *CSVHandle, req *ModifyRequest, totalRows int64) (*ModifyResult, error) {
|
||
// 1. 创建临时文件
|
||
tempFile, err := os.CreateTemp(handle.tempDir, "temp_*.csv")
|
||
if err != nil {
|
||
return nil, fmt.Errorf("创建临时文件失败: %v", err)
|
||
}
|
||
defer func() {
|
||
tempFile.Close()
|
||
os.Remove(tempFile.Name())
|
||
}()
|
||
|
||
// 2. 复制并修改文件
|
||
modifiedCount, err := mgr.copyAndModifyRow(handle, tempFile, req)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("复制修改文件失败: %v", err)
|
||
}
|
||
|
||
// 3. 关闭临时文件以确保数据写入磁盘
|
||
if err := tempFile.Close(); err != nil {
|
||
return nil, fmt.Errorf("关闭临时文件失败: %v", err)
|
||
}
|
||
|
||
// 4. 原子替换原文件
|
||
if err := mgr.atomicReplaceFile(tempFile.Name(), handle.Filename); err != nil {
|
||
// 尝试恢复备份
|
||
mgr.restoreFromBackup(handle)
|
||
return nil, fmt.Errorf("替换文件失败: %v", err)
|
||
}
|
||
|
||
// 5. 重新打开文件
|
||
handle.mu.Lock()
|
||
if handle.File != nil {
|
||
handle.File.Close()
|
||
handle.File = nil
|
||
handle.IsOpen = false
|
||
}
|
||
|
||
if err := mgr.openFile(handle); err != nil {
|
||
handle.mu.Unlock()
|
||
return nil, fmt.Errorf("重新打开文件失败: %v", err)
|
||
}
|
||
|
||
// 更新总行数
|
||
handle.TotalRows = totalRows
|
||
handle.mu.Unlock()
|
||
|
||
return &ModifyResult{
|
||
Success: true,
|
||
Message: fmt.Sprintf("修改第%d行成功", req.RowNumber),
|
||
RowsAffected: modifiedCount,
|
||
}, nil
|
||
}
|
||
|
||
// copyAndModifyRow 复制文件并修改指定行
|
||
func (mgr *CSVManager) copyAndModifyRow(handle *CSVHandle, dst *os.File, req *ModifyRequest) (int64, error) {
|
||
// 打开源文件
|
||
src, err := os.Open(handle.Filename)
|
||
if err != nil {
|
||
return 0, err
|
||
}
|
||
defer src.Close()
|
||
|
||
// 创建CSV读写器
|
||
csvReader := csv.NewReader(src)
|
||
csvReader.Comma = handle.Delimiter
|
||
csvReader.LazyQuotes = true
|
||
|
||
csvWriter := csv.NewWriter(dst)
|
||
csvWriter.Comma = handle.Delimiter
|
||
|
||
var rowCount int64 = 0
|
||
var modifiedCount int64 = 0
|
||
|
||
for {
|
||
row, err := csvReader.Read()
|
||
if err == io.EOF {
|
||
break
|
||
}
|
||
if err != nil {
|
||
csvWriter.Flush()
|
||
return modifiedCount, fmt.Errorf("读取第%d行失败: %v", rowCount+1, err)
|
||
}
|
||
|
||
rowCount++
|
||
|
||
// 检查是否是目标行(考虑表头)
|
||
targetRowNumber := req.RowNumber
|
||
if handle.HasHeader {
|
||
targetRowNumber = req.RowNumber + 1
|
||
}
|
||
|
||
if rowCount == targetRowNumber {
|
||
// 修改这一行
|
||
row = req.NewRow
|
||
modifiedCount++
|
||
}
|
||
|
||
// 写入行
|
||
if err := csvWriter.Write(row); err != nil {
|
||
csvWriter.Flush()
|
||
return modifiedCount, fmt.Errorf("写入第%d行失败: %v", rowCount, err)
|
||
}
|
||
}
|
||
|
||
// 确保所有数据写入文件
|
||
if err := csvWriter.Error(); err != nil {
|
||
return modifiedCount, fmt.Errorf("CSV写入错误: %v", err)
|
||
}
|
||
|
||
csvWriter.Flush()
|
||
return modifiedCount, nil
|
||
}
|
||
|
||
// modifyCellInternal 修改单元格
|
||
func (mgr *CSVManager) modifyCellInternal(handle *CSVHandle, req *ModifyRequest, totalRows int64) (*ModifyResult, error) {
|
||
// 1. 读取目标行
|
||
targetRow, err := mgr.readSpecificRow(handle, req.RowNumber)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("读取目标行失败: %v", err)
|
||
}
|
||
|
||
// 2. 修改单元格
|
||
if req.ColumnIndex >= 0 && req.ColumnIndex < len(targetRow) {
|
||
targetRow[req.ColumnIndex] = req.NewCellValue
|
||
} else {
|
||
return nil, fmt.Errorf("列索引超出范围: %d", req.ColumnIndex)
|
||
}
|
||
|
||
// 3. 构建修改请求(转换为整行修改)
|
||
rowReq := &ModifyRequest{
|
||
HandleID: req.HandleID,
|
||
RowNumber: req.RowNumber,
|
||
ModifyType: ModifyRow,
|
||
NewRow: targetRow,
|
||
}
|
||
|
||
// 4. 调用整行修改
|
||
return mgr.modifyRowInternal(handle, rowReq, totalRows)
|
||
}
|
||
|
||
// insertRowInternal 插入行
|
||
func (mgr *CSVManager) insertRowInternal(handle *CSVHandle, req *ModifyRequest, totalRows int64) (*ModifyResult, error) {
|
||
// 创建临时文件
|
||
tempFile, err := os.CreateTemp(handle.tempDir, "insert_*.csv")
|
||
if err != nil {
|
||
return nil, fmt.Errorf("创建临时文件失败: %v", err)
|
||
}
|
||
defer func() {
|
||
tempFile.Close()
|
||
os.Remove(tempFile.Name())
|
||
}()
|
||
|
||
// 打开源文件
|
||
src, err := os.Open(handle.Filename)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer src.Close()
|
||
|
||
// 创建CSV读写器
|
||
csvReader := csv.NewReader(src)
|
||
csvReader.Comma = handle.Delimiter
|
||
|
||
csvWriter := csv.NewWriter(tempFile)
|
||
csvWriter.Comma = handle.Delimiter
|
||
|
||
var rowCount int64 = 0
|
||
|
||
for {
|
||
row, err := csvReader.Read()
|
||
if err == io.EOF {
|
||
break
|
||
}
|
||
if err != nil {
|
||
csvWriter.Flush()
|
||
return nil, fmt.Errorf("读取第%d行失败: %v", rowCount+1, err)
|
||
}
|
||
|
||
rowCount++
|
||
|
||
// 写入当前行
|
||
if err := csvWriter.Write(row); err != nil {
|
||
csvWriter.Flush()
|
||
return nil, fmt.Errorf("写入第%d行失败: %v", rowCount, err)
|
||
}
|
||
|
||
// 检查是否到达插入位置(考虑表头)
|
||
insertPosition := req.RowNumber
|
||
if handle.HasHeader {
|
||
insertPosition = req.RowNumber + 1
|
||
}
|
||
|
||
if rowCount == insertPosition {
|
||
// 插入新行
|
||
if err := csvWriter.Write(req.NewRow); err != nil {
|
||
csvWriter.Flush()
|
||
return nil, fmt.Errorf("插入新行失败: %v", err)
|
||
}
|
||
}
|
||
}
|
||
|
||
// 如果是最后一行之后插入
|
||
adjustedTotalRows := totalRows
|
||
if handle.HasHeader {
|
||
adjustedTotalRows = totalRows - 1
|
||
}
|
||
|
||
if req.RowNumber == adjustedTotalRows+1 {
|
||
// 在文件末尾插入
|
||
if err := csvWriter.Write(req.NewRow); err != nil {
|
||
csvWriter.Flush()
|
||
return nil, fmt.Errorf("在末尾插入新行失败: %v", err)
|
||
}
|
||
}
|
||
|
||
// 确保所有数据写入
|
||
csvWriter.Flush()
|
||
if err := csvWriter.Error(); err != nil {
|
||
return nil, fmt.Errorf("CSV写入错误: %v", err)
|
||
}
|
||
|
||
// 关闭临时文件
|
||
if err := tempFile.Close(); err != nil {
|
||
return nil, fmt.Errorf("关闭临时文件失败: %v", err)
|
||
}
|
||
|
||
// 原子替换原文件
|
||
handle.mu.Lock()
|
||
if handle.File != nil {
|
||
handle.File.Close()
|
||
handle.File = nil
|
||
handle.IsOpen = false
|
||
}
|
||
|
||
if err := mgr.atomicReplaceFile(tempFile.Name(), handle.Filename); err != nil {
|
||
handle.mu.Unlock()
|
||
mgr.restoreFromBackup(handle)
|
||
return nil, fmt.Errorf("替换文件失败: %v", err)
|
||
}
|
||
|
||
// 重新打开文件
|
||
if err := mgr.openFile(handle); err != nil {
|
||
handle.mu.Unlock()
|
||
return nil, fmt.Errorf("重新打开文件失败: %v", err)
|
||
}
|
||
|
||
// 更新总行数
|
||
handle.TotalRows = totalRows + 1
|
||
handle.mu.Unlock()
|
||
|
||
return &ModifyResult{
|
||
Success: true,
|
||
Message: fmt.Sprintf("在第%d行后插入成功", req.RowNumber),
|
||
RowsAffected: 1,
|
||
}, nil
|
||
}
|
||
|
||
// deleteRowInternal 删除行
|
||
func (mgr *CSVManager) deleteRowInternal(handle *CSVHandle, req *ModifyRequest, totalRows int64) (*ModifyResult, error) {
|
||
// 创建临时文件
|
||
tempFile, err := os.CreateTemp(handle.tempDir, "delete_*.csv")
|
||
if err != nil {
|
||
return nil, fmt.Errorf("创建临时文件失败: %v", err)
|
||
}
|
||
defer func() {
|
||
tempFile.Close()
|
||
os.Remove(tempFile.Name())
|
||
}()
|
||
|
||
// 打开源文件
|
||
src, err := os.Open(handle.Filename)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer src.Close()
|
||
|
||
// 创建CSV读写器
|
||
csvReader := csv.NewReader(src)
|
||
csvReader.Comma = handle.Delimiter
|
||
|
||
csvWriter := csv.NewWriter(tempFile)
|
||
csvWriter.Comma = handle.Delimiter
|
||
|
||
var rowCount int64 = 0
|
||
var deletedCount int64 = 0
|
||
|
||
for {
|
||
row, err := csvReader.Read()
|
||
if err == io.EOF {
|
||
break
|
||
}
|
||
if err != nil {
|
||
csvWriter.Flush()
|
||
return nil, fmt.Errorf("读取第%d行失败: %v", rowCount+1, err)
|
||
}
|
||
|
||
rowCount++
|
||
|
||
// 检查是否是要删除的行(考虑表头)
|
||
deleteRowNumber := req.RowNumber
|
||
if handle.HasHeader {
|
||
deleteRowNumber = req.RowNumber + 1
|
||
}
|
||
|
||
if rowCount == deleteRowNumber {
|
||
// 跳过这一行(不写入)
|
||
deletedCount++
|
||
continue
|
||
}
|
||
|
||
// 写入行
|
||
if err := csvWriter.Write(row); err != nil {
|
||
csvWriter.Flush()
|
||
return nil, fmt.Errorf("写入第%d行失败: %v", rowCount, err)
|
||
}
|
||
}
|
||
|
||
// 确保所有数据写入
|
||
csvWriter.Flush()
|
||
if err := csvWriter.Error(); err != nil {
|
||
return nil, fmt.Errorf("CSV写入错误: %v", err)
|
||
}
|
||
|
||
// 关闭临时文件
|
||
if err := tempFile.Close(); err != nil {
|
||
return nil, fmt.Errorf("关闭临时文件失败: %v", err)
|
||
}
|
||
|
||
// 原子替换原文件
|
||
handle.mu.Lock()
|
||
if handle.File != nil {
|
||
handle.File.Close()
|
||
handle.File = nil
|
||
handle.IsOpen = false
|
||
}
|
||
|
||
if err := mgr.atomicReplaceFile(tempFile.Name(), handle.Filename); err != nil {
|
||
handle.mu.Unlock()
|
||
mgr.restoreFromBackup(handle)
|
||
return nil, fmt.Errorf("替换文件失败: %v", err)
|
||
}
|
||
|
||
// 重新打开文件
|
||
if err := mgr.openFile(handle); err != nil {
|
||
handle.mu.Unlock()
|
||
return nil, fmt.Errorf("重新打开文件失败: %v", err)
|
||
}
|
||
|
||
// 更新总行数
|
||
handle.TotalRows = totalRows - deletedCount
|
||
handle.mu.Unlock()
|
||
|
||
return &ModifyResult{
|
||
Success: true,
|
||
Message: fmt.Sprintf("删除第%d行成功", req.RowNumber),
|
||
RowsAffected: deletedCount,
|
||
}, nil
|
||
}
|
||
|
||
// readSpecificRow 读取指定行
|
||
func (mgr *CSVManager) readSpecificRow(handle *CSVHandle, rowNumber int64) ([]string, error) {
|
||
// 保存当前位置
|
||
currentPos, err := handle.File.Seek(0, io.SeekCurrent)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
defer handle.File.Seek(currentPos, io.SeekStart)
|
||
|
||
// 重置到文件开始
|
||
if _, err := handle.File.Seek(0, 0); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 创建新的CSV阅读器
|
||
reader := csv.NewReader(handle.File)
|
||
reader.Comma = handle.Delimiter
|
||
|
||
var rowCount int64 = 0
|
||
targetRowNumber := rowNumber
|
||
if handle.HasHeader {
|
||
targetRowNumber = rowNumber + 1
|
||
}
|
||
|
||
for {
|
||
row, err := reader.Read()
|
||
if err == io.EOF {
|
||
break
|
||
}
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
rowCount++
|
||
if rowCount == targetRowNumber {
|
||
return row, nil
|
||
}
|
||
}
|
||
|
||
return nil, fmt.Errorf("行号超出范围: %d", rowNumber)
|
||
}
|
||
|
||
// ========================== 工具方法 ==========================
|
||
|
||
// atomicReplaceFile 原子替换文件
|
||
func (mgr *CSVManager) atomicReplaceFile(src, dst string) error {
|
||
// 先尝试原子重命名(Unix/Linux上这是原子的)
|
||
if err := os.Rename(src, dst); err == nil {
|
||
return nil
|
||
}
|
||
|
||
// 如果重命名失败(比如Windows或跨分区),使用复制方式
|
||
// 创建临时目标文件
|
||
tmpDst := dst + ".tmp"
|
||
if err := mgr.copyFile(src, tmpDst); err != nil {
|
||
return err
|
||
}
|
||
|
||
// 重命名为目标文件
|
||
if err := os.Rename(tmpDst, dst); err != nil {
|
||
os.Remove(tmpDst)
|
||
return err
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// copyFile 复制文件
|
||
func (mgr *CSVManager) copyFile(src, dst string) error {
|
||
source, err := os.Open(src)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer source.Close()
|
||
|
||
destination, err := os.Create(dst)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer destination.Close()
|
||
|
||
_, err = io.Copy(destination, source)
|
||
return err
|
||
}
|
||
|
||
// restoreFromBackup 从备份恢复
|
||
func (mgr *CSVManager) restoreFromBackup(handle *CSVHandle) error {
|
||
handle.mu.Lock()
|
||
defer handle.mu.Unlock()
|
||
|
||
if len(handle.backupFiles) == 0 {
|
||
return fmt.Errorf("没有可用的备份文件")
|
||
}
|
||
|
||
// 使用最新的备份
|
||
latestBackup := handle.backupFiles[len(handle.backupFiles)-1]
|
||
|
||
// 关闭当前文件
|
||
if handle.File != nil {
|
||
handle.File.Close()
|
||
handle.File = nil
|
||
handle.IsOpen = false
|
||
}
|
||
|
||
// 恢复备份
|
||
if err := mgr.atomicReplaceFile(latestBackup, handle.Filename); err != nil {
|
||
return fmt.Errorf("恢复备份失败: %v", err)
|
||
}
|
||
|
||
// 重新打开文件
|
||
return mgr.openFile(handle)
|
||
}
|
||
|
||
// ========================== 其他操作 ==========================
|
||
|
||
// GetRow 获取指定行数据
|
||
func (mgr *CSVManager) GetRow(handleID int64, rowNumber int64) ([]string, error) {
|
||
handle, err := mgr.GetHandle(handleID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 获取文件级读锁
|
||
fileLock := mgr.getFileLock(handle.Filename)
|
||
fileLock.RLock()
|
||
defer fileLock.RUnlock()
|
||
|
||
return mgr.readSpecificRow(handle, rowNumber)
|
||
}
|
||
|
||
// UndoLastModify 撤销最后一次修改
|
||
func (mgr *CSVManager) UndoLastModify(handleID int64) (*ModifyResult, error) {
|
||
handle, err := mgr.GetHandle(handleID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 获取文件级写锁
|
||
fileLock := mgr.getFileLock(handle.Filename)
|
||
fileLock.Lock()
|
||
defer fileLock.Unlock()
|
||
|
||
handle.mu.Lock()
|
||
defer handle.mu.Unlock()
|
||
|
||
if len(handle.backupFiles) == 0 {
|
||
return &ModifyResult{
|
||
Success: false,
|
||
Message: "没有可撤销的修改",
|
||
}, nil
|
||
}
|
||
|
||
// 关闭当前文件
|
||
if handle.File != nil {
|
||
handle.File.Close()
|
||
handle.File = nil
|
||
handle.IsOpen = false
|
||
}
|
||
|
||
// 获取最新的备份
|
||
latestBackup := handle.backupFiles[len(handle.backupFiles)-1]
|
||
|
||
// 恢复文件
|
||
if err := mgr.atomicReplaceFile(latestBackup, handle.Filename); err != nil {
|
||
return &ModifyResult{
|
||
Success: false,
|
||
Message: fmt.Sprintf("恢复文件失败: %v", err),
|
||
}, err
|
||
}
|
||
|
||
// 重新打开文件
|
||
if err := mgr.openFile(handle); err != nil {
|
||
return &ModifyResult{
|
||
Success: false,
|
||
Message: fmt.Sprintf("重新打开文件失败: %v", err),
|
||
}, err
|
||
}
|
||
|
||
// 移除已使用的备份
|
||
handle.backupFiles = handle.backupFiles[:len(handle.backupFiles)-1]
|
||
|
||
// 重新计算总行数
|
||
totalRows, _ := mgr.calculateTotalRowsWithLock(handle)
|
||
fmt.Printf("总行数: %d\n", totalRows)
|
||
|
||
return &ModifyResult{
|
||
Success: true,
|
||
Message: "撤销成功",
|
||
RowsAffected: 1,
|
||
BackupFile: latestBackup,
|
||
}, nil
|
||
}
|
||
|
||
// CleanupBackups 清理备份文件
|
||
func (mgr *CSVManager) CleanupBackups(handleID int64) error {
|
||
handle, err := mgr.GetHandle(handleID)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
handle.mu.Lock()
|
||
defer handle.mu.Unlock()
|
||
|
||
// 清理所有备份文件
|
||
for _, backupFile := range handle.backupFiles {
|
||
os.Remove(backupFile)
|
||
}
|
||
handle.backupFiles = nil
|
||
|
||
// 清理临时目录
|
||
if handle.tempDir != "" {
|
||
os.RemoveAll(handle.tempDir)
|
||
handle.tempDir = ""
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// ========================== 句柄管理 ==========================
|
||
|
||
// Close 关闭CSV句柄
|
||
func (h *CSVHandle) Close() error {
|
||
h.mu.Lock()
|
||
defer h.mu.Unlock()
|
||
|
||
h.writeMu.Lock()
|
||
defer h.writeMu.Unlock()
|
||
|
||
if !h.IsOpen {
|
||
return nil
|
||
}
|
||
|
||
// 停止自动关闭计时器
|
||
if h.autoCloseTimer != nil {
|
||
h.autoCloseTimer.Stop()
|
||
h.autoCloseTimer = nil
|
||
}
|
||
|
||
// 关闭文件
|
||
if h.File != nil {
|
||
if err := h.File.Close(); err != nil {
|
||
return err
|
||
}
|
||
h.File = nil
|
||
}
|
||
|
||
h.CSVReader = nil
|
||
h.IsOpen = false
|
||
|
||
return nil
|
||
}
|
||
|
||
// startAutoClose 启动自动关闭计时器
|
||
func (h *CSVHandle) startAutoClose(timeout time.Duration, mgr *CSVManager) {
|
||
h.autoCloseTimer = time.AfterFunc(timeout, func() {
|
||
h.mu.Lock()
|
||
defer h.mu.Unlock()
|
||
|
||
// 检查是否长时间未访问
|
||
if h.IsOpen && time.Since(h.AccessTime) > timeout {
|
||
h.Close()
|
||
// 从管理器移除
|
||
mgr.handles.Delete(h.ID)
|
||
}
|
||
})
|
||
}
|
||
|
||
// GetHeader 获取表头
|
||
func (h *CSVHandle) GetHeader() []string {
|
||
h.mu.RLock()
|
||
defer h.mu.RUnlock()
|
||
return h.Header
|
||
}
|
||
|
||
// GetInfo 获取句柄信息
|
||
func (h *CSVHandle) GetInfo() map[string]interface{} {
|
||
h.mu.RLock()
|
||
defer h.mu.RUnlock()
|
||
|
||
return map[string]interface{}{
|
||
"id": h.ID,
|
||
"filename": h.Filename,
|
||
"delimiter": string(h.Delimiter),
|
||
"has_header": h.HasHeader,
|
||
"is_open": h.IsOpen,
|
||
"open_time": h.OpenTime,
|
||
"access_time": h.AccessTime,
|
||
"access_count": h.AccessCount,
|
||
"total_rows": h.TotalRows,
|
||
"header": h.Header,
|
||
}
|
||
}
|
||
|
||
// CloseHandle 关闭指定句柄
|
||
func (mgr *CSVManager) CloseHandle(handleID int64) error {
|
||
value, ok := mgr.handles.Load(handleID)
|
||
if !ok {
|
||
return fmt.Errorf("句柄不存在: %d", handleID)
|
||
}
|
||
|
||
handle := value.(*CSVHandle)
|
||
if err := handle.Close(); err != nil {
|
||
return err
|
||
}
|
||
|
||
// 清理文件锁
|
||
mgr.fileLocks.Delete(handle.Filename)
|
||
|
||
mgr.handles.Delete(handleID)
|
||
return nil
|
||
}
|
||
|
||
// CloseAllHandles 关闭所有句柄
|
||
func (mgr *CSVManager) CloseAllHandles() {
|
||
mgr.handles.Range(func(key, value interface{}) bool {
|
||
handle := value.(*CSVHandle)
|
||
handle.Close()
|
||
mgr.fileLocks.Delete(handle.Filename)
|
||
mgr.handles.Delete(key)
|
||
return true
|
||
})
|
||
}
|
||
|
||
// ========================== 简便函数 ==========================
|
||
|
||
//// OpenCSVFile 打开CSV文件
|
||
//func OpenCSVFile(filename string, delimiter rune, hasHeader bool) (int64, error) {
|
||
// return GetManager().OpenCSVFile(filename, delimiter, hasHeader, "utf-8")
|
||
//}
|
||
|
||
// GetCSVHandle 获取CSV句柄
|
||
func GetCSVHandle(handleID int64) (*CSVHandle, error) {
|
||
return GetManager().GetHandle(handleID)
|
||
}
|
||
|
||
// CloseCSVHandle 关闭CSV句柄
|
||
func CloseCSVHandle(handleID int64) error {
|
||
return GetManager().CloseHandle(handleID)
|
||
}
|
||
|
||
// ModifyCSVRow 修改CSV行
|
||
func ModifyCSVRow(handleID int64, rowNumber int64, newRow []string) (*ModifyResult, error) {
|
||
return GetManager().ModifyRow(handleID, rowNumber, newRow)
|
||
}
|
||
|
||
// ModifyCSVCell 修改CSV单元格
|
||
func ModifyCSVCell(handleID int64, rowNumber int64, columnIndex int, newValue string) (*ModifyResult, error) {
|
||
return GetManager().ModifyCell(handleID, rowNumber, columnIndex, newValue)
|
||
}
|
||
|
||
// GetCSVRow 获取CSV行
|
||
func GetCSVRow(handleID int64, rowNumber int64) ([]string, error) {
|
||
return GetManager().GetRow(handleID, rowNumber)
|
||
}
|
||
|
||
// UndoLastCSVModify 撤销最后修改
|
||
func UndoLastCSVModify(handleID int64) (*ModifyResult, error) {
|
||
return GetManager().UndoLastModify(handleID)
|
||
}
|
||
|
||
// CleanupCSVBackups 清理CSV备份
|
||
func CleanupCSVBackups(handleID int64) error {
|
||
return GetManager().CleanupBackups(handleID)
|
||
}
|
||
|
||
// UpdateCSVRowSafe 修改csv文件行数据
|
||
func (mgr *CSVManager) UpdateCSVRowSafe(handleID int64, rowNum int, newRow []string) error {
|
||
getHandle, err := mgr.GetHandle(handleID)
|
||
if err != nil {
|
||
return fmt.Errorf("获取句柄信息失败: %v", err)
|
||
}
|
||
|
||
// 1. 创建临时文件
|
||
tempDir := filepath.Dir(getHandle.Filename)
|
||
if tempDir == "" {
|
||
tempDir = "."
|
||
}
|
||
|
||
tempFile, err := os.CreateTemp(tempDir, "temp_*.csv")
|
||
if err != nil {
|
||
return fmt.Errorf("创建临时文件失败: %v", err)
|
||
}
|
||
tempFileName := tempFile.Name()
|
||
|
||
// 确保临时文件被关闭和清理
|
||
defer func() {
|
||
tempFile.Close()
|
||
// 如果临时文件还存在(替换失败),则清理它
|
||
if _, err := os.Stat(tempFileName); err == nil {
|
||
os.Remove(tempFileName)
|
||
}
|
||
}()
|
||
|
||
// 2. 读取原始文件
|
||
sourceFile, err := os.Open(getHandle.Filename)
|
||
if err != nil {
|
||
return fmt.Errorf("打开源文件失败: %v", err)
|
||
}
|
||
defer sourceFile.Close()
|
||
|
||
// 3. 读取并处理数据
|
||
reader := csv.NewReader(sourceFile)
|
||
allRows, err := reader.ReadAll()
|
||
if err != nil {
|
||
return fmt.Errorf("读取CSV失败: %v", err)
|
||
}
|
||
|
||
// 4. 验证并更新
|
||
if rowNum < 1 || rowNum > len(allRows) {
|
||
return fmt.Errorf("行号 %d 超出范围 (1-%d)", rowNum, len(allRows))
|
||
}
|
||
|
||
// 显示修改前后的对比
|
||
oldRow := allRows[rowNum-1]
|
||
fmt.Printf("修改前第 %d 行: %v\n", rowNum, oldRow)
|
||
fmt.Printf("修改后第 %d 行: %v\n", rowNum, newRow)
|
||
|
||
allRows[rowNum-1] = newRow
|
||
|
||
// 5. 写入临时文件
|
||
writer := csv.NewWriter(tempFile)
|
||
if err := writer.WriteAll(allRows); err != nil {
|
||
return fmt.Errorf("写入临时文件失败: %v", err)
|
||
}
|
||
writer.Flush()
|
||
|
||
if err := writer.Error(); err != nil {
|
||
return fmt.Errorf("刷新写入失败: %v", err)
|
||
}
|
||
|
||
// 6. 确保数据写入磁盘
|
||
if err := tempFile.Sync(); err != nil {
|
||
return fmt.Errorf("同步文件失败: %v", err)
|
||
}
|
||
tempFile.Close()
|
||
|
||
// 7. 使用复制而不是重命名(解决Windows文件占用问题)
|
||
if err := copyFile(tempFileName, getHandle.Filename); err != nil {
|
||
return fmt.Errorf("复制文件失败: %v", err)
|
||
}
|
||
|
||
fmt.Printf("✅ 成功更新第 %d 行,文件已直接更新\n", rowNum)
|
||
return nil
|
||
}
|
||
|
||
// copyFile 复制文件内容
|
||
func copyFile(src, dst string) error {
|
||
// 打开源文件
|
||
source, err := os.Open(src)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer source.Close()
|
||
|
||
// 创建目标文件
|
||
destination, err := os.Create(dst)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer destination.Close()
|
||
|
||
// 复制内容
|
||
_, err = io.Copy(destination, source)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
// 确保数据写入磁盘
|
||
err = destination.Sync()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// CSV响应结构体
|
||
type CSVResponse struct {
|
||
Success bool `json:"success"`
|
||
Message string `json:"message,omitempty"`
|
||
Data interface{} `json:"data,omitempty"`
|
||
}
|
||
|
||
// ===================== C 函数导入 ==================
|
||
// OpenCSVFile 打开/创建CSV文件
|
||
//
|
||
//export OpenCSVFile
|
||
func OpenCSVFile(filename *C.char, delimiter C.char, hasHeader C.int) *C.char {
|
||
goFilename := C.GoString(filename)
|
||
goDelimiter := rune(delimiter)
|
||
var hasHeaderBool bool
|
||
if hasHeader != 0 {
|
||
hasHeaderBool = true
|
||
}
|
||
hasHeaderBool = false
|
||
handle, err := GetManager().OpenCSVFile(goFilename, goDelimiter, hasHeaderBool)
|
||
response := struct {
|
||
Handle int64 `json:"handle"`
|
||
}{
|
||
Handle: handle,
|
||
}
|
||
var csvResponse CSVResponse
|
||
if err != nil {
|
||
csvResponse = CSVResponse{
|
||
Success: false,
|
||
Message: err.Error(),
|
||
}
|
||
} else {
|
||
csvResponse = CSVResponse{
|
||
Success: true,
|
||
Data: response,
|
||
}
|
||
}
|
||
// 转换为JSON字符串
|
||
jsonData, err := json.Marshal(csvResponse)
|
||
if err != nil {
|
||
// 如果JSON序列化失败,返回错误信息
|
||
csvResponse = CSVResponse{
|
||
Success: false,
|
||
Message: fmt.Sprintf("JSON序列化失败: %v", err),
|
||
}
|
||
errorJson, _ := json.Marshal(csvResponse)
|
||
return C.CString(string(errorJson))
|
||
}
|
||
return C.CString(string(jsonData))
|
||
}
|
||
|
||
// UpdateCSVRowSafe 修改csv文件行数据
|
||
//
|
||
//export UpdateCSVRowSafe
|
||
func UpdateCSVRowSafe(handleID C.longlong, rowNum C.int, newRow *C.char) *C.char {
|
||
goHandle := int64(handleID)
|
||
goRowNum := int(rowNum)
|
||
goNewRow := C.GoString(newRow)
|
||
var row []string
|
||
var csvResponse CSVResponse
|
||
// 解析JSON
|
||
err := json.Unmarshal([]byte(goNewRow), &row)
|
||
if err != nil {
|
||
csvResponse = CSVResponse{
|
||
Success: false,
|
||
Message: fmt.Sprintf("解析JSON失败: %v", err),
|
||
}
|
||
errorJson, _ := json.Marshal(csvResponse)
|
||
return C.CString(string(errorJson))
|
||
}
|
||
// 修改csv行数据
|
||
err = GetManager().UpdateCSVRowSafe(goHandle, goRowNum, row)
|
||
if err != nil {
|
||
csvResponse = CSVResponse{
|
||
Success: false,
|
||
Message: fmt.Sprintf(err.Error()),
|
||
}
|
||
errorJson, _ := json.Marshal(csvResponse)
|
||
return C.CString(string(errorJson))
|
||
} else {
|
||
csvResponse = CSVResponse{
|
||
Success: true,
|
||
}
|
||
}
|
||
// 转换为JSON字符串
|
||
jsonData, err := json.Marshal(csvResponse)
|
||
if err != nil {
|
||
// 如果JSON序列化失败,返回错误信息
|
||
csvResponse = CSVResponse{
|
||
Success: false,
|
||
Message: fmt.Sprintf("JSON序列化失败: %v", err),
|
||
}
|
||
errorJson, _ := json.Marshal(csvResponse)
|
||
return C.CString(string(errorJson))
|
||
}
|
||
return C.CString(string(jsonData))
|
||
}
|
||
|
||
// 主函数
|
||
func main() {
|
||
//fmt.Println("=== CSV句柄管理器测试 ===")
|
||
//
|
||
//filename := "csv/taskLog.csv"
|
||
//fmt.Printf("1. 创建测试文件: %s\n", filename)
|
||
//
|
||
//// 2. 打开CSV文件
|
||
//handleID, err := OpenCSVFile(filename, ',', true)
|
||
//if err != nil {
|
||
// fmt.Printf("打开文件失败: %v\n", err)
|
||
// return
|
||
//}
|
||
//defer CloseCSVHandle(handleID)
|
||
//
|
||
//fmt.Printf("2. 打开文件成功,句柄ID: %d\n", handleID)
|
||
//
|
||
////// 3. 获取句柄信息
|
||
////handle, err := GetCSVHandle(handleID)
|
||
////if err != nil {
|
||
//// fmt.Printf("获取句柄失败: %v\n", err)
|
||
//// return
|
||
////}
|
||
////
|
||
////info := handle.GetInfo()
|
||
////fmt.Printf("3. 文件信息:\n")
|
||
////fmt.Printf(" - 文件名: %s\n", info["filename"])
|
||
////fmt.Printf(" - 总行数: %v\n", info["total_rows"])
|
||
////fmt.Printf(" - 表头: %v\n", info["header"])
|
||
//
|
||
////// 准备新的行数据
|
||
////newData := []string{"9787115524539", "20.00", "20", "上传成功", ""}
|
||
////err = UpdateCSVRowSafes(handleID, 6, newData)
|
||
////if err != nil {
|
||
//// fmt.Printf("错误: %v\n", err)
|
||
////} else {
|
||
//// fmt.Println("CSV文件更新成功!")
|
||
////}
|
||
//
|
||
//// 4. 读取数据
|
||
//fmt.Println("4. 读取测试:")
|
||
//row3, err := GetCSVRow(handleID, 100000)
|
||
//if err != nil {
|
||
// fmt.Printf(" 读取第100000行失败: %v\n", err)
|
||
//} else {
|
||
// fmt.Printf(" 第100000行数据: %v\n", row3)
|
||
//}
|
||
//
|
||
////// 5. 修改测试
|
||
////fmt.Println("5. 修改测试:")
|
||
////newRow := []string{"9787115524539", "20.00", "1", "上传成功", ""}
|
||
////result, err := ModifyCSVRow(handleID, 3, newRow)
|
||
////if err != nil {
|
||
//// fmt.Printf(" 修改失败: %v\n", err)
|
||
////} else {
|
||
//// fmt.Printf(" 修改结果: %s (影响行数: %d)\n", result.Message, result.RowsAffected)
|
||
////
|
||
//// // 验证修改
|
||
//// modifiedRow, _ := GetCSVRow(handleID, 3)
|
||
//// fmt.Printf(" 修改后的第3行: %v\n", modifiedRow)
|
||
////}
|
||
//
|
||
////// 6. 单元格修改测试
|
||
////fmt.Println("6. 单元格修改测试:")
|
||
////cellResult, err := ModifyCSVCell(handleID, 5, 1, "35")
|
||
////if err != nil {
|
||
//// fmt.Printf(" 单元格修改失败: %v\n", err)
|
||
////} else {
|
||
//// fmt.Printf(" 单元格修改结果: %s\n", cellResult.Message)
|
||
////
|
||
//// // 验证修改
|
||
//// cellModifiedRow, _ := GetCSVRow(handleID, 5)
|
||
//// fmt.Printf(" 修改后的第5行: %v\n", cellModifiedRow)
|
||
////}
|
||
////
|
||
////// 7. 并发测试
|
||
////fmt.Println("7. 并发测试:")
|
||
////TestConcurrentOperations(handleID)
|
||
////
|
||
////fmt.Println("=== 测试完成 ===")
|
||
}
|
||
|
||
// 下面是一个更简单的版本,根据你的具体需求可以选择使用
|
||
func WriteCSVSimple(filename string, data [][]string) error {
|
||
if len(data) == 0 {
|
||
return fmt.Errorf("写入的数据不能为空")
|
||
}
|
||
|
||
var file *os.File
|
||
var err error
|
||
var fileMode int
|
||
|
||
// 检查文件是否存在和有内容
|
||
if info, err := os.Stat(filename); err == nil && info.Size() > 0 {
|
||
// 文件存在且有内容,追加模式
|
||
fileMode = os.O_APPEND | os.O_WRONLY
|
||
} else {
|
||
// 文件不存在或为空,创建模式(会清空已有内容)
|
||
fileMode = os.O_CREATE | os.O_WRONLY | os.O_TRUNC
|
||
}
|
||
|
||
// 打开或创建文件
|
||
file, err = os.OpenFile(filename, fileMode, 0755)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer file.Close()
|
||
|
||
// 写入CSV数据
|
||
writer := csv.NewWriter(file)
|
||
if err := writer.WriteAll(data); err != nil {
|
||
return err
|
||
}
|
||
writer.Flush()
|
||
|
||
return writer.Error()
|
||
}
|