mirror of
				https://github.com/mindoc-org/mindoc.git
				synced 2025-11-01 01:06:42 +08:00 
			
		
		
		
	feat: 支持上传视频功能
This commit is contained in:
		| @@ -106,9 +106,9 @@ func GetDefaultCover() string { | ||||
| 	return URLForWithCdnImage(web.AppConfig.DefaultString("cover", "/static/images/book.jpg")) | ||||
| } | ||||
|  | ||||
| // 获取允许的商城文件的类型. | ||||
| // 获取允许的上传文件的类型. | ||||
| func GetUploadFileExt() []string { | ||||
| 	ext := web.AppConfig.DefaultString("upload_file_ext", "png|jpg|jpeg|gif|txt|doc|docx|pdf") | ||||
| 	ext := web.AppConfig.DefaultString("upload_file_ext", "png|jpg|jpeg|gif|txt|doc|docx|pdf|mp4") | ||||
|  | ||||
| 	temp := strings.Split(ext, "|") | ||||
|  | ||||
| @@ -201,7 +201,7 @@ func GetExportOutputPath() string { | ||||
| 	return exportOutputPath | ||||
| } | ||||
|  | ||||
| // 判断是否是允许商城的文件类型. | ||||
| // 判断是否是允许上传的文件类型. | ||||
| func IsAllowUploadFileExt(ext string) bool { | ||||
|  | ||||
| 	if strings.HasPrefix(ext, ".") { | ||||
|   | ||||
| @@ -7,6 +7,7 @@ import ( | ||||
| 	"html/template" | ||||
| 	"image/png" | ||||
| 	"io" | ||||
| 	"mime/multipart" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 	"os" | ||||
| @@ -486,41 +487,25 @@ func (c *DocumentController) Upload() { | ||||
| 		c.JsonResult(6001, i18n.Tr(c.Lang, "message.param_error")) | ||||
| 	} | ||||
|  | ||||
| 	name := "editormd-file-file" | ||||
|  | ||||
| 	// file, moreFile, err := c.GetFile(name) | ||||
| 	// if err == http.ErrMissingFile || moreFile == nil { | ||||
| 	// 	name = "editormd-image-file" | ||||
| 	// 	file, moreFile, err = c.GetFile(name) | ||||
| 	// 	if err == http.ErrMissingFile || moreFile == nil { | ||||
| 	// 		c.JsonResult(6003, i18n.Tr(c.Lang, "message.upload_file_empty")) | ||||
| 	// 		return | ||||
| 	// 	} | ||||
| 	// } | ||||
| 	// ****3xxx | ||||
| 	files, err := c.GetFiles(name) | ||||
| 	if err == http.ErrMissingFile { | ||||
| 		name = "editormd-image-file" | ||||
| 		files, err = c.GetFiles(name) | ||||
| 		if err == http.ErrMissingFile { | ||||
| 			// c.JsonResult(6003, i18n.Tr(c.Lang, "message.upload_file_empty")) | ||||
| 			// return | ||||
| 			name = "file" | ||||
| 			files, err = c.GetFiles(name) | ||||
| 			// logs.Info(files) | ||||
| 			if err == http.ErrMissingFile { | ||||
| 				c.JsonResult(6003, i18n.Tr(c.Lang, "message.upload_file_empty")) | ||||
| 				return | ||||
| 			} | ||||
| 	names := []string{"editormd-file-file", "editormd-image-file", "file", "editormd-resource-file"} | ||||
| 	currentName := "" | ||||
| 	var files []*multipart.FileHeader | ||||
| 	for _, name := range names { | ||||
| 		file, err := c.GetFiles(name) | ||||
| 		if err != nil { | ||||
| 			continue | ||||
| 		} | ||||
| 		if len(file) > 0 && err == nil { | ||||
| 			currentName = name | ||||
| 			files = append(files, file...) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// if err != nil { | ||||
| 	// 	http.Error(w, err.Error(), http.StatusNoContent) | ||||
| 	// 	return | ||||
| 	// } | ||||
| 	// jMap := make(map[string]interface{}) | ||||
| 	// s := []map[int]interface{}{} | ||||
| 	if len(files) == 0 { | ||||
| 		c.JsonResult(6003, i18n.Tr(c.Lang, "message.upload_file_empty")) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	result2 := []map[string]interface{}{} | ||||
| 	var result map[string]interface{} | ||||
| 	for i, _ := range files { | ||||
| @@ -528,24 +513,6 @@ func (c *DocumentController) Upload() { | ||||
| 		file, err := files[i].Open() | ||||
|  | ||||
| 		defer file.Close() | ||||
| 		// if err != nil { | ||||
| 		// 	http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 		// 	return | ||||
| 		// } | ||||
| 		// 	//create destination file making sure the path is writeable. | ||||
| 		// 	dst, err := os.Create("upload/" + files[i].Filename) | ||||
| 		// 	defer dst.Close() | ||||
| 		// 	if err != nil { | ||||
| 		// 		http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 		// 		return | ||||
| 		// 	} | ||||
| 		// 	//copy the uploaded file to the destination file | ||||
| 		// 	if _, err := io.Copy(dst, file); err != nil { | ||||
| 		// 		http.Error(w, err.Error(), http.StatusInternalServerError) | ||||
| 		// 		return | ||||
| 		// 	} | ||||
| 		// } | ||||
| 		// **** | ||||
|  | ||||
| 		if err != nil { | ||||
| 			c.JsonResult(6002, err.Error()) | ||||
| @@ -619,19 +586,25 @@ func (c *DocumentController) Upload() { | ||||
| 		filePath := filepath.Join(conf.WorkingDirectory, "uploads", identify) | ||||
|  | ||||
| 		//将图片和文件分开存放 | ||||
| 		// if filetil.IsImageExt(moreFile.Filename) { | ||||
| 		attachment := models.NewAttachment() | ||||
| 		var strategy filetil.FileTypeStrategy | ||||
| 		if filetil.IsImageExt(files[i].Filename) { | ||||
| 			filePath = filepath.Join(filePath, "images", fileName+ext) | ||||
| 			strategy = filetil.ImageStrategy{} | ||||
| 			attachment.ResourceType = "image" | ||||
| 		} else if filetil.IsVideoExt(files[i].Filename) { | ||||
| 			strategy = filetil.VideoStrategy{} | ||||
| 			attachment.ResourceType = "video" | ||||
| 		} else { | ||||
| 			filePath = filepath.Join(filePath, "files", fileName+ext) | ||||
| 			strategy = filetil.DefaultStrategy{} | ||||
| 			attachment.ResourceType = "file" | ||||
| 		} | ||||
|  | ||||
| 		filePath = strategy.GetFilePath(filePath, fileName, ext) | ||||
|  | ||||
| 		path := filepath.Dir(filePath) | ||||
|  | ||||
| 		_ = os.MkdirAll(path, os.ModePerm) | ||||
|  | ||||
| 		// err = c.SaveToFile(name, filePath) // frome beego controller.go: savetofile it only operates the first one of mutil-upload form file field. | ||||
|  | ||||
| 		//copy the uploaded file to the destination file | ||||
| 		dst, err := os.Create(filePath) | ||||
| 		defer dst.Close() | ||||
| @@ -640,12 +613,6 @@ func (c *DocumentController) Upload() { | ||||
| 			c.JsonResult(6005, i18n.Tr(c.Lang, "message.failed")) | ||||
| 		} | ||||
|  | ||||
| 		// if err != nil { | ||||
| 		// 	logs.Error("保存文件失败 -> ", err) | ||||
| 		// 	c.JsonResult(6005, i18n.Tr(c.Lang, "message.failed")) | ||||
| 		// } | ||||
|  | ||||
| 		attachment := models.NewAttachment() | ||||
| 		attachment.BookId = bookId | ||||
| 		// attachment.FileName = moreFile.Filename | ||||
| 		attachment.FileName = files[i].Filename | ||||
| @@ -662,8 +629,7 @@ func (c *DocumentController) Upload() { | ||||
| 			attachment.DocumentId = docId | ||||
| 		} | ||||
|  | ||||
| 		// if filetil.IsImageExt(moreFile.Filename) { | ||||
| 		if filetil.IsImageExt(files[i].Filename) { | ||||
| 		if filetil.IsImageExt(files[i].Filename) || filetil.IsVideoExt(files[i].Filename) { | ||||
| 			attachment.HttpPath = "/" + strings.Replace(strings.TrimPrefix(filePath, conf.WorkingDirectory), "\\", "/", -1) | ||||
| 			if strings.HasPrefix(attachment.HttpPath, "//") { | ||||
| 				attachment.HttpPath = conf.URLForWithCdnImage(string(attachment.HttpPath[1:])) | ||||
| @@ -689,18 +655,19 @@ func (c *DocumentController) Upload() { | ||||
| 			} | ||||
| 		} | ||||
| 		result = map[string]interface{}{ | ||||
| 			"errcode":   0, | ||||
| 			"success":   1, | ||||
| 			"message":   "ok", | ||||
| 			"url":       attachment.HttpPath, | ||||
| 			"link":      attachment.HttpPath, | ||||
| 			"alt":       attachment.FileName, | ||||
| 			"is_attach": isAttach, | ||||
| 			"attach":    attachment, | ||||
| 			"errcode":       0, | ||||
| 			"success":       1, | ||||
| 			"message":       "ok", | ||||
| 			"url":           attachment.HttpPath, | ||||
| 			"link":          attachment.HttpPath, | ||||
| 			"alt":           attachment.FileName, | ||||
| 			"is_attach":     isAttach, | ||||
| 			"attach":        attachment, | ||||
| 			"resource_type": attachment.ResourceType, | ||||
| 		} | ||||
| 		result2 = append(result2, result) | ||||
| 	} | ||||
| 	if name == "file" { | ||||
| 	if currentName == "file" { | ||||
| 		// froala单图片上传 | ||||
| 		c.Ctx.Output.JSON(result, true, false) | ||||
| 	} else { | ||||
|   | ||||
| @@ -33,6 +33,7 @@ type Attachment struct { | ||||
| 	FileExt      string    `orm:"column(file_ext);size(50);description(文件后缀)" json:"file_ext"` | ||||
| 	CreateTime   time.Time `orm:"type(datetime);column(create_time);auto_now_add;description(创建时间)" json:"create_time"` | ||||
| 	CreateAt     int       `orm:"column(create_at);type(int);description(创建人id)" json:"create_at"` | ||||
| 	ResourceType string    `orm:"-" json:"resource_type"` | ||||
| } | ||||
|  | ||||
| // TableName 获取对应上传附件数据库表名. | ||||
|   | ||||
| @@ -437,6 +437,86 @@ function uploadImage($id, $callback) { | ||||
|     }); | ||||
| } | ||||
|  | ||||
|  | ||||
| function uploadResource($id, $callback) { | ||||
|     locales = { | ||||
|         'zh-CN': { | ||||
|             unsupportType: '不支持的图片/视频格式', | ||||
|             uploadFailed: '图片/视频上传失败' | ||||
|         }, | ||||
|         'en': { | ||||
|             unsupportType: 'Unsupport image/video type', | ||||
|             uploadFailed: 'Upload image/video failed' | ||||
|         } | ||||
|     } | ||||
|     /** 粘贴上传的资源 **/ | ||||
|     document.getElementById($id).addEventListener('paste', function (e) { | ||||
|         if (e.clipboardData && e.clipboardData.items) { | ||||
|             var clipboard = e.clipboardData; | ||||
|             for (var i = 0, len = clipboard.items.length; i < len; i++) { | ||||
|                 if (clipboard.items[i].kind === 'file' || clipboard.items[i].type.indexOf('image') > -1) { | ||||
|  | ||||
|                     var resource = clipboard.items[i].getAsFile(); | ||||
|  | ||||
|                     var fileName = String((new Date()).valueOf()); | ||||
|                     console.log(resource.type) | ||||
|                     switch (resource.type) { | ||||
|                         case "image/png" : | ||||
|                             fileName += ".png"; | ||||
|                             break; | ||||
|                         case "image/jpg" : | ||||
|                             fileName += ".jpg"; | ||||
|                             break; | ||||
|                         case "image/jpeg" : | ||||
|                             fileName += ".jpeg"; | ||||
|                             break; | ||||
|                         case "image/gif" : | ||||
|                             fileName += ".gif"; | ||||
|                             break; | ||||
|                         case "video/mp4": | ||||
|                             fileName += ".mp4"; | ||||
|                             break; | ||||
|                         default : | ||||
|                             layer.msg(locales[lang].unsupportType); | ||||
|                             return; | ||||
|                     } | ||||
|                     var form = new FormData(); | ||||
|  | ||||
|                     form.append('editormd-resource-file', resource, fileName); | ||||
|  | ||||
|                     var layerIndex = 0; | ||||
|  | ||||
|                     $.ajax({ | ||||
|                         url: window.imageUploadURL, | ||||
|                         type: "POST", | ||||
|                         dataType: "json", | ||||
|                         data: form, | ||||
|                         processData: false, | ||||
|                         contentType: false, | ||||
|                         beforeSend: function () { | ||||
|                             layerIndex = $callback('before'); | ||||
|                         }, | ||||
|                         error: function () { | ||||
|                             layer.close(layerIndex); | ||||
|                             $callback('error'); | ||||
|                             layer.msg(locales[lang].uploadFailed); | ||||
|                         }, | ||||
|                         success: function (data) { | ||||
|                             layer.close(layerIndex); | ||||
|                             $callback('success', data); | ||||
|                             if (data.errcode !== 0) { | ||||
|                                 layer.msg(data.message); | ||||
|                             } | ||||
|  | ||||
|                         } | ||||
|                     }); | ||||
|                     e.preventDefault(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * 初始化代码高亮 | ||||
|  */ | ||||
|   | ||||
| @@ -245,18 +245,20 @@ $(function () { | ||||
|  | ||||
|             //如果没有选中节点则选中默认节点 | ||||
|             openLastSelectedNode(); | ||||
|             uploadImage("docEditor", function ($state, $res) { | ||||
|             uploadResource("docEditor", function ($state, $res) { | ||||
|                 if ($state === "before") { | ||||
|                     return layer.load(1, { | ||||
|                         shade: [0.1, '#fff'] // 0.1 透明度的白色背景 | ||||
|                     }); | ||||
|                 } else if ($state === "success") { | ||||
|                     // if ($res.errcode === 0) { | ||||
|                         // var value = ''; | ||||
|                     // 3xxx 20240602 | ||||
|                     if ($res[0].errcode === 0) { | ||||
|                         var value = ''; | ||||
|                         window.editor.insertValue(value); | ||||
|                         if ($res[0].resource_type === 'video') { | ||||
|                             let value = `<video controls><source src="${$res[0].url}" type="video/mp4"></video>`; | ||||
|                             window.editor.insertValue(value); | ||||
|                         } else { | ||||
|                             let value = ''; | ||||
|                             window.editor.insertValue(value); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             }); | ||||
|   | ||||
| @@ -1,21 +1,43 @@ | ||||
| package filetil | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"io/ioutil" | ||||
| 	"math" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"io" | ||||
| 	"fmt" | ||||
| 	"math" | ||||
| 	"io/ioutil" | ||||
| 	"bytes" | ||||
| ) | ||||
|  | ||||
| //================================== | ||||
| //更多文件和目录的操作,使用filepath包和os包 | ||||
| //================================== | ||||
|  | ||||
| //返回的目录扫描结果 | ||||
| type FileTypeStrategy interface { | ||||
| 	GetFilePath(filePath, fileName, ext string) string | ||||
| } | ||||
|  | ||||
| type ImageStrategy struct{} | ||||
|  | ||||
| func (i ImageStrategy) GetFilePath(filePath, fileName, ext string) string { | ||||
| 	return filepath.Join(filePath, "images", fileName+ext) | ||||
| } | ||||
|  | ||||
| type VideoStrategy struct{} | ||||
|  | ||||
| func (v VideoStrategy) GetFilePath(filePath, fileName, ext string) string { | ||||
| 	return filepath.Join(filePath, "videos", fileName+ext) | ||||
| } | ||||
|  | ||||
| type DefaultStrategy struct{} | ||||
|  | ||||
| func (d DefaultStrategy) GetFilePath(filePath, fileName, ext string) string { | ||||
| 	return filepath.Join(filePath, "files", fileName+ext) | ||||
| } | ||||
|  | ||||
| // 返回的目录扫描结果 | ||||
| type FileList struct { | ||||
| 	IsDir   bool   //是否是目录 | ||||
| 	Path    string //文件路径 | ||||
| @@ -25,10 +47,10 @@ type FileList struct { | ||||
| 	ModTime int64  //文件修改时间戳 | ||||
| } | ||||
|  | ||||
| //目录扫描 | ||||
| //@param			dir			需要扫描的目录 | ||||
| //@return			fl			文件列表 | ||||
| //@return			err			错误 | ||||
| // 目录扫描 | ||||
| // @param			dir			需要扫描的目录 | ||||
| // @return			fl			文件列表 | ||||
| // @return			err			错误 | ||||
| func ScanFiles(dir string) (fl []FileList, err error) { | ||||
| 	err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { | ||||
| 		if err == nil { | ||||
| @@ -47,7 +69,7 @@ func ScanFiles(dir string) (fl []FileList, err error) { | ||||
| 	return | ||||
| } | ||||
|  | ||||
| //拷贝文件 | ||||
| // 拷贝文件 | ||||
| func CopyFile(source string, dst string) (err error) { | ||||
| 	sourceFile, err := os.Open(source) | ||||
| 	if err != nil { | ||||
| @@ -56,17 +78,16 @@ func CopyFile(source string, dst string) (err error) { | ||||
|  | ||||
| 	defer sourceFile.Close() | ||||
|  | ||||
| 	_,err = os.Stat(filepath.Dir(dst)) | ||||
| 	_, err = os.Stat(filepath.Dir(dst)) | ||||
|  | ||||
| 	if err != nil { | ||||
| 		if os.IsNotExist(err) { | ||||
| 			os.MkdirAll(filepath.Dir(dst),0766) | ||||
| 		}else{ | ||||
| 			os.MkdirAll(filepath.Dir(dst), 0766) | ||||
| 		} else { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	destFile, err := os.Create(dst) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| @@ -86,7 +107,7 @@ func CopyFile(source string, dst string) (err error) { | ||||
| 	return | ||||
| } | ||||
|  | ||||
| //拷贝目录 | ||||
| // 拷贝目录 | ||||
| func CopyDir(source string, dest string) (err error) { | ||||
|  | ||||
| 	// get properties of source dir | ||||
| @@ -107,7 +128,7 @@ func CopyDir(source string, dest string) (err error) { | ||||
|  | ||||
| 	for _, obj := range objects { | ||||
|  | ||||
| 		sourceFilePointer := filepath.Join(source , obj.Name()) | ||||
| 		sourceFilePointer := filepath.Join(source, obj.Name()) | ||||
|  | ||||
| 		destinationFilePointer := filepath.Join(dest, obj.Name()) | ||||
|  | ||||
| @@ -205,15 +226,15 @@ func Round(val float64, places int) float64 { | ||||
| 	return t | ||||
| } | ||||
|  | ||||
| //判断指定目录下是否存在指定后缀的文件 | ||||
| func HasFileOfExt(path string,exts []string) bool { | ||||
| // 判断指定目录下是否存在指定后缀的文件 | ||||
| func HasFileOfExt(path string, exts []string) bool { | ||||
| 	err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error { | ||||
| 		if !info.IsDir() { | ||||
|  | ||||
| 			ext := filepath.Ext(info.Name()) | ||||
|  | ||||
| 			for _,item := range exts { | ||||
| 				if strings.EqualFold(ext,item) { | ||||
| 			for _, item := range exts { | ||||
| 				if strings.EqualFold(ext, item) { | ||||
| 					return os.ErrExist | ||||
| 				} | ||||
| 			} | ||||
| @@ -224,6 +245,7 @@ func HasFileOfExt(path string,exts []string) bool { | ||||
|  | ||||
| 	return err == os.ErrExist | ||||
| } | ||||
|  | ||||
| // IsImageExt 判断是否是图片后缀 | ||||
| func IsImageExt(filename string) bool { | ||||
| 	ext := filepath.Ext(filename) | ||||
| @@ -232,25 +254,32 @@ func IsImageExt(filename string) bool { | ||||
| 		strings.EqualFold(ext, ".jpeg") || | ||||
| 		strings.EqualFold(ext, ".png") || | ||||
| 		strings.EqualFold(ext, ".gif") || | ||||
| 		strings.EqualFold(ext,".svg") || | ||||
| 		strings.EqualFold(ext,".bmp") || | ||||
| 		strings.EqualFold(ext,".webp") | ||||
| 		strings.EqualFold(ext, ".svg") || | ||||
| 		strings.EqualFold(ext, ".bmp") || | ||||
| 		strings.EqualFold(ext, ".webp") | ||||
| } | ||||
| //忽略字符串中的BOM头 | ||||
| func ReadFileAndIgnoreUTF8BOM(filename string) ([]byte,error) { | ||||
|  | ||||
| 	data,err := ioutil.ReadFile(filename) | ||||
| // IsImageExt 判断是否是视频后缀 | ||||
| func IsVideoExt(filename string) bool { | ||||
| 	ext := filepath.Ext(filename) | ||||
|  | ||||
| 	return strings.EqualFold(ext, ".mp4") | ||||
| } | ||||
|  | ||||
| // 忽略字符串中的BOM头 | ||||
| func ReadFileAndIgnoreUTF8BOM(filename string) ([]byte, error) { | ||||
|  | ||||
| 	data, err := ioutil.ReadFile(filename) | ||||
| 	if err != nil { | ||||
| 		return nil,err | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if data == nil { | ||||
| 		return nil,nil | ||||
| 		return nil, nil | ||||
| 	} | ||||
| 	data = bytes.Replace(data,[]byte("\r"),[]byte(""),-1) | ||||
| 	data = bytes.Replace(data, []byte("\r"), []byte(""), -1) | ||||
| 	if len(data) >= 3 && data[0] == 0xef && data[1] == 0xbb && data[2] == 0xbf { | ||||
| 		return data[3:],err | ||||
| 		return data[3:], err | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	return data,nil | ||||
| 	return data, nil | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 zhanzhenping
					zhanzhenping