Files
nms_cxy/lib/services/file.go

427 lines
10 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package services
import (
"bufio"
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
"strconv"
"strings"
"be.ems/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
}