You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

205 lines
5.6 KiB

package localchat
import (
"encoding/base64"
"fmt"
"godo/libs"
"io"
"log"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"time"
)
// 合并文件分片
func mergeFiles(fileName string, totalParts int, chatDir string) error {
var parts []io.Reader
for i := 1; i <= totalParts; i++ {
filePath := fmt.Sprintf("%v%v_%v.part", chatDir, fileName, i)
file, err := os.Open(filePath)
if err != nil {
return fmt.Errorf("failed to open part %d: %w", i, err)
}
defer file.Close()
parts = append(parts, file)
}
mergedFilePath := fmt.Sprintf("%vmerged_%v", chatDir, fileName)
mergedFile, err := os.Create(mergedFilePath)
if err != nil {
return fmt.Errorf("failed to create merged file: %w", err)
}
defer mergedFile.Close()
_, err = io.Copy(mergedFile, io.MultiReader(parts...))
if err != nil {
return fmt.Errorf("failed to merge files: %w", err)
}
// 合并后清理分片文件
for i := 1; i <= totalParts; i++ {
os.Remove(fmt.Sprintf("%v%v_%v.part", chatDir, fileName, i))
}
return nil
}
func UploadBigFileHandler(w http.ResponseWriter, r *http.Request, msg Message) {
chatDir, err := GetChatPath()
if err != nil {
http.Error(w, "Failed to get chat path", http.StatusInternalServerError)
return
}
// 创建或打开临时文件以写入分片
tempFilePath := fmt.Sprintf("%v%v_%v.part", chatDir, msg.FileInfo.FileName, msg.FileInfo.PartNumber)
out, err := os.Create(tempFilePath)
if err != nil {
log.Printf("Failed to create temp file: %v", err)
http.Error(w, "Failed to create temp file", http.StatusInternalServerError)
return
}
defer out.Close()
// 将请求体的内容写入临时文件
_, err = io.Copy(out, r.Body)
if err != nil {
log.Printf("Failed to write file part: %v", err)
http.Error(w, "Failed to write file part", http.StatusInternalServerError)
return
}
// 更新上传状态
uploadStatus.Lock()
uploadStatus.Status[msg.FileInfo.FileName]++
if uploadStatus.Status[msg.FileInfo.FileName] == msg.FileInfo.TotalParts {
// 所有分片上传完成,触发合并
go func() {
err := mergeFiles(msg.FileInfo.FileName, msg.FileInfo.TotalParts, chatDir)
if err != nil {
log.Printf("Failed to merge files for %v: %v", msg.FileInfo.FileName, err)
} else {
log.Printf("Merged file %v successfully", msg.FileInfo.FileName)
}
// 清理状态记录
delete(uploadStatus.Status, msg.FileInfo.FileName)
msg.Content = "uploaded"
messageChan <- msg
}()
}
uploadStatus.Unlock()
// 返回成功响应
w.WriteHeader(http.StatusCreated)
fmt.Fprintln(w, "File part uploaded successfully")
}
// SaveContentToFile 保存内容到文件并返回UploadInfo结构体
func SaveContentToFile(content, fileName string) (UploadInfo, error) {
uploadBaseDir, err := GetChatPath()
if err != nil {
return UploadInfo{}, err
}
appDir, err := libs.GetOsDir()
if err != nil {
return UploadInfo{}, err
}
// 去除文件名中的空格
fileNameWithoutSpaces := strings.ReplaceAll(fileName, " ", "_")
fileNameWithoutSpaces = strings.ReplaceAll(fileNameWithoutSpaces, "/", "")
fileNameWithoutSpaces = strings.ReplaceAll(fileNameWithoutSpaces, `\`, "")
// 提取文件名和扩展名
// 查找最后一个点的位置
lastDotIndex := strings.LastIndexByte(fileNameWithoutSpaces, '.')
// 如果找到点,则提取扩展名,否则视为没有扩展名
ext := ""
if lastDotIndex != -1 {
ext = fileNameWithoutSpaces[lastDotIndex:]
fileNameWithoutSpaces = fileNameWithoutSpaces[:lastDotIndex]
} else {
ext = ""
}
randFileName := fmt.Sprintf("%s_%s%s", fileNameWithoutSpaces, strconv.FormatInt(time.Now().UnixNano(), 10), ext)
savePath := filepath.Join(uploadBaseDir, randFileName)
if err := os.MkdirAll(filepath.Dir(savePath), 0755); err != nil {
return UploadInfo{}, err
}
if err := os.WriteFile(savePath, []byte(content), 0644); err != nil {
return UploadInfo{}, err
}
content = string(content)
// 检查文件内容是否以"link::"开头
if !strings.HasPrefix(content, "link::") {
content = base64.StdEncoding.EncodeToString([]byte(content))
}
return UploadInfo{
Name: fileNameWithoutSpaces,
SavePath: strings.TrimPrefix(savePath, appDir),
Content: content,
CreatedAt: time.Now(),
}, nil
}
// MultiUploadHandler 处理多文件上传请求
func MultiUploadHandler(w http.ResponseWriter, r *http.Request) {
if err := r.ParseMultipartForm(10000 << 20); err != nil {
http.Error(w, "Failed to parse multipart form", http.StatusBadRequest)
return
}
files := r.MultipartForm.File["files"]
if len(files) == 0 {
http.Error(w, "No file parts in the request", http.StatusBadRequest)
return
}
fileInfoList := make([]UploadInfo, 0, len(files))
for _, fileHeader := range files {
file, err := fileHeader.Open()
if err != nil {
http.Error(w, "Failed to open uploaded file", http.StatusBadRequest)
continue
}
defer file.Close()
content, err := io.ReadAll(file)
if err != nil {
http.Error(w, "Failed to read uploaded file", http.StatusBadRequest)
continue
}
//log.Printf(string(content))
// 保存上传的文件内容
info, err := SaveContentToFile(string(content), fileHeader.Filename)
if err != nil {
http.Error(w, "Failed to save uploaded file", http.StatusBadRequest)
continue
}
log.Println(info.SavePath)
//info.SavePath = savePath
fileInfoList = append(fileInfoList, info)
}
user := UserInfo{
IP: r.FormValue("ip"),
Hostname: r.FormValue("hostname"),
}
msg := Message{
Type: "file",
Content: "file recieved",
SenderInfo: user,
FileList: fileInfoList,
}
messageChan <- msg
w.WriteHeader(http.StatusOK)
fmt.Fprintln(w, "File send successfully")
//serv.Res(serv.Response{Code: 0, Data: fileInfoList}, w)
}