mirror of https://gitee.com/godoos/godoos.git
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
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)
|
|
}
|
|
|