package services import ( "bufio" "bytes" "fmt" "io" "mime/multipart" "net/http" "os" "strconv" "strings" "ems.agt/lib/log" ) const ( RootPath = "uploads/" ChunkRootPath = "uploads_tmp/" ) var ( // FilesMax 限制上传文件的大小为7 MB FilesMax int64 = 32 << 20 // ValuesMax 限制POST字段内容的大小 ValuesMax int64 = 512 ) func GetPostFile(w http.ResponseWriter, r *http.Request) { //获取文件流,第三个返回值是错误对象 file, header, _ := r.FormFile("file") //读取文件流为[]byte b, err := io.ReadAll(file) if err != nil { log.Error("Failed to ReadAll:", err) ResponseInternalServerError500ProcessError(w, err) return } //把文件保存到指定位置 err = os.WriteFile("./upload/test.zip", b, 0644) if err != nil { log.Error("Failed to WriteFile:", err) ResponseInternalServerError500ProcessError(w, err) return } //输出上传时文件名 log.Debug("filename:", header.Filename) } func GetUploadFile(w http.ResponseWriter, r *http.Request) { log.Debug("GetUploadFile processing...") file, err := os.Create("./test.zip") if err != nil { log.Error("Failed to Create:", err) ResponseInternalServerError500ProcessError(w, err) return } _, err = io.Copy(file, r.Body) if err != nil { log.Error("Failed to Copy:", err) ResponseInternalServerError500ProcessError(w, err) return } } func GetUploadFormFile(w http.ResponseWriter, r *http.Request) { // 设置最大的内存限制为32MB r.ParseMultipartForm(32 << 20) file, handler, err := r.FormFile("file") if err != nil { log.Error("Failed to FormFile:", err) ResponseInternalServerError500ProcessError(w, err) return } defer file.Close() log.Debug("Header:%v", handler.Header) f, err := os.OpenFile("./"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666) if err != nil { log.Error("Failed to OpenFile:", err) ResponseInternalServerError500ProcessError(w, err) return } defer f.Close() _, err = io.Copy(f, file) if err != nil { log.Error("Failed to Copy:", err) ResponseInternalServerError500ProcessError(w, err) return } log.Debug("File uploaded successfully:", handler.Filename) } func HandleUploadFile(r *http.Request, path, newFileName string) (string, error) { var filePath, fileName string reader, err := r.MultipartReader() if err != nil { log.Error("Failed to MultipartReader:", err) return "", err } for { part, err := reader.NextPart() if err == io.EOF { break } else if err != nil { log.Error("Failed to NextPart:", err) return "", err } log.Debugf("FileName=[%s], FormName=[%s]", part.FileName(), part.FormName()) if part.FileName() == "" { // this is FormData data, _ := io.ReadAll(part) log.Debugf("FormData=[%s]", string(data)) } else { // This is FileData if newFileName != "" { fileName = newFileName } else { fileName = part.FileName() } err := os.MkdirAll(path, os.ModePerm) if err != nil { log.Error("Failed to Mkdir:", err) return "", err } filePath = path + "/" + fileName file, err := os.Create(filePath) if err != nil { log.Error("Failed to Create:", err) return "", err } defer file.Close() _, err = io.Copy(file, part) if err != nil { log.Error("Failed to Copy:", err) return "", err } } } return fileName, nil } type UploadMultiFileData struct { SoftwareFileName string `json:"softwareFileName"` CmsFileName string `json:"cmsFileName"` Datas map[string][]string `json:"datas"` } func HandleUploadMultiFile(r *http.Request, path, newFileName string) (*UploadMultiFileData, error) { fileData := new(UploadMultiFileData) // 解析multipart/form-data请求 err := r.ParseMultipartForm(100 << 20) // 100MB if err != nil { return fileData, err } // 获取文件和数据 softwareFile := r.MultipartForm.File["file"] cmsFile := r.MultipartForm.File["cms"] fileData.Datas = r.MultipartForm.Value // 处理文件 if len(softwareFile) > 0 { file := softwareFile[0] // 打开文件 f, err := file.Open() if err != nil { return fileData, err } defer f.Close() // 创建本地文件 dst, err := os.Create(path + "/" + file.Filename) if err != nil { return fileData, err } defer dst.Close() fileData.SoftwareFileName = file.Filename // 将文件内容拷贝到本地文件 _, err = io.Copy(dst, f) if err != nil { return fileData, err } } // 处理文件 if len(cmsFile) > 0 { file := cmsFile[0] // 打开文件 f, err := file.Open() if err != nil { return fileData, err } defer f.Close() // 创建本地文件 dst, err := os.Create(path + "/" + file.Filename) if err != nil { return fileData, err } defer dst.Close() fileData.CmsFileName = file.Filename // 将文件内容拷贝到本地文件 _, err = io.Copy(dst, f) if err != nil { return fileData, err } } return fileData, nil } func HandleUploadFormFile(w http.ResponseWriter, r *http.Request) { r.ParseMultipartForm(32 << 20) //mForm := r.MultipartForm for k := range r.MultipartForm.File { // k is the key of file part file, fileHeader, err := r.FormFile(k) if err != nil { fmt.Println("inovke FormFile error:", err) return } defer file.Close() fmt.Printf("the uploaded file: name[%s], size[%d], header[%#v]\n", fileHeader.Filename, fileHeader.Size, fileHeader.Header) // store uploaded file into local path localFileName := "./upload/" + fileHeader.Filename out, err := os.Create(localFileName) if err != nil { fmt.Printf("failed to open the file %s for writing", localFileName) return } defer out.Close() _, err = io.Copy(out, file) if err != nil { fmt.Printf("copy file err:%s\n", err) return } fmt.Printf("file %s uploaded ok\n", fileHeader.Filename) } } func PostFileHandler(w http.ResponseWriter, r *http.Request) { fmt.Println("PostFileHandler processing... ") if !strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data") { // 不支持的 Content-Type 类型 fmt.Println("Invalid Content-Type: ", r.Header.Get("Content-Type")) http.Error(w, " Unsupported Content-Type Types", http.StatusBadRequest) return } // 整个请求的主体大小设置为7.5Mb r.Body = http.MaxBytesReader(w, r.Body, FilesMax+ValuesMax) reader, err := r.MultipartReader() if err != nil { fmt.Println(err) http.Error(w, err.Error(), http.StatusBadRequest) return } for { // A Part represents a single part in a multipart body. part, err := reader.NextPart() if err != nil { if err == io.EOF { break } http.Error(w, err.Error(), http.StatusInternalServerError) return } fileName := part.FileName() formName := part.FormName() var buf = &bytes.Buffer{} // 非文件字段部分大小限制验证(非文件字段,go中filename会是空) if fileName == "" { // "请求主体中非文件字段" + formName + "超出大小限制" var limitError = fmt.Sprintf("Non-file field %s in request body exceeds size limit", formName) err = uploadSizeLimit(buf, part, ValuesMax, limitError) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } continue } // 文件字段部分大小限制验证 // "请求主体中文件字段" + fileName + "超出大小限制" var limitError = fmt.Sprintf("File field %s in request body exceeds size limit", fileName) err = uploadSizeLimit(buf, part, FilesMax, limitError) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } // 文件创建部分 if err := uploadFileHandle(r.Header, fileName, buf); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } // 非逻辑内容,仅为测试使用 var chunkNumber = r.Header.Get("chunk-number") if chunkNumber == "" { // "文件"+fileName+"上传成功" http.Error(w, fmt.Sprintf("File %s uploaded successfully", fileName), http.StatusOK) } else { // "分片文件 %s %s 上传成功" http.Error(w, fmt.Sprintf("Sliced file %s %s uploaded successfully.", fileName, chunkNumber), http.StatusOK) } } } // 上传内容大小限制 func uploadSizeLimit(buf *bytes.Buffer, part *multipart.Part, maxLimit int64, limitError string) error { n, err := io.CopyN(buf, part, maxLimit+1) if err != nil && err != io.EOF { fmt.Println("PostFileHandler:", err) return err } maxLimit -= n if maxLimit < 0 { return fmt.Errorf(limitError) } return nil } // uploadFileHandle handle upload file func uploadFileHandle(header http.Header, fileName string, buf *bytes.Buffer) error { var chunkNumberStr = header.Get("chunk-number") // 1.普通文件上传处理 if chunkNumberStr == "" { //创建文件并写入文件内容 return createFile(RootPath+fileName, buf.Bytes()) } // 2.分片文件上传处理 //2.1读取分片编号 chunkNumber, err := strconv.Atoi(chunkNumberStr) if err != nil { return err } //2.2创建分片文件并写入分片内容 if err := createFile(fmt.Sprintf(ChunkRootPath+fileName+"%d.chunk", chunkNumber), buf.Bytes()); err != nil { return err } //2.3确认是否上传完毕 if header.Get("chunk-final") == "true" { //2.4合并文件 if err := mergeChunkFiles(fileName); err != nil { return err } //2.5删除分片 for i := 0; ; i++ { chunFileName := fmt.Sprintf(ChunkRootPath+fileName+"%d.chunk", i) err := os.Remove(chunFileName) if err != nil { if os.IsNotExist(err) { break } return err } } } return nil } // 创建文件并写入内容 func createFile(fileName string, res []byte) error { newFile, err := os.Create(fileName) if err != nil { return err } defer func() { _ = newFile.Close() }() bufferedWriter := bufio.NewWriter(newFile) _, err = bufferedWriter.Write(res) if err != nil && err != io.EOF { return err } return bufferedWriter.Flush() } // 合并分片文件 func mergeChunkFiles(fileName string) error { var ( n int64 err error ) finalFile, err := os.Create(RootPath + fileName) if err != nil { return err } defer finalFile.Close() // 将分片内容写入最终文件 for i := 0; ; i++ { chunFile, err := os.Open(fmt.Sprintf(ChunkRootPath+fileName+"%d.chunk", i)) if err != nil { if os.IsNotExist(err) { break } return err } n, err = io.Copy(finalFile, chunFile) if err != nil { return err } err = chunFile.Close() if err != nil { return err } if n < 1 { break } } return nil }