427 lines
10 KiB
Go
427 lines
10 KiB
Go
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
|
||
}
|