【学习笔记】Vue + ElementUI +Go-admin 上传文件

4 篇文章 0 订阅
2 篇文章 1 订阅

前端

上传-不使用el-upload组件

upload(row) {
      // 1、未审核时,上传也出现两个row的情况
      // 2、在驳回=>撤销驳回=>再上传的时候,会出现row有两个对象的情况

      // row只有上传的时候不正常,其他驳回、撤销都正常
      // 出问题时直接打印row,发现传过来的也是两个对象。
      if (row.rejectTag === '已驳回') {
        alert('该报告已被驳回,请先撤销驳回')
        return
      }
      console.log('文件内容', row.files, '状态标签', row.rejectTag, '报告id', row.reportId)
      if (row.files !== 'rejected clean' && row.files !== 'null' && row.files !== '') {
        alert('您已上传侵权报告')
        return
      }
      this.$refs.uploadInput.click()
      console.log(this.$refs)
      this.$refs.uploadInput.onchange = e => {
        const formData = new FormData()
        formData.append('file', e.target.files[0])
        Upload(formData).then(res => {
          const path = res.data.data.path
          const id = row.reportId
          updateReport({
            reportId: id,
            filesOpt: 'add',
            files: [path]
          }).then(res => {
            console.log(res.data)
            this.getList()
            setTimeout(() => {
              // 这样才能正确关闭
              this.msg = this.$message({
                duration: 1000,
                type: 'success',
                message: '提交成功'
              })
            }, 0)
          })
        })
      }
    }

this.$refs

ref 写在标签上时:this.$refs.xxx获取的是添加了ref="xxx"标签对应的dom元素

 
<el-button icon="el-icon-upload" size="small" type="primary" @click="handleUploadFile">
          上传文件
          <input v-show="false" ref="uploadInput" type="file">
        </el-button>

如果这样写,会出现错误,不报错,但是结果会永远显示row=3的时候的数据,这是为什么呢?
同时如果通过搜索得到一行数据,我们也不能在搜索的结果上传,这是为什么呢?

在这里插入图片描述
因为我们在一整个el-table中只需要一个上传键,一次一个用户只能点一个按钮,一个按钮对应一个文件上传,所以整个文件不需要在遍历罗列上传按钮的时候把this.refs也遍历罗列,只需要一个就可以了。所以把<input v-show="false" ref="uploadInput" type="file">放在所有el-table的前面,事先声明且跟el-table是平级的。
在这里插入图片描述

this.refs如果写在上传的el-button里面,则打印this.refs的时候也会出现两次ref的名字。也就是调用了两次this.ref指向的dom元素(input),所以我们对于this.refs进行操作的时候,也操作了两次(click、onchange等函数里写到的操作),所以返回来的也是两个对象。而最后改动的数据是第二个对象(错误带回的对象,并不是原来需要操作的对象),导致即录入不了数据库,也无法上传成功。

上传-使用el-upload 组件

element-ui官网的上传文档

预览-仅支持图片预览

其他的预览需要office或者wps的一些插件支持,暂时没有研究

下载-axios

暂时没有研究

后端

1 文件上传

type FileResponse struct {
	Size     int64  `json:"size"`
	Path     string `json:"path"`
	FullPath string `json:"full_path"`
	Name     string `json:"name"`
	Type     string `json:"type"`
}

const path = "你的文件地址"

type File struct {
	api.Api
}
// UploadFile 上传图片
// @Summary 上传图片
// @Description 获取form-data
// @Tags 公共接口
// @Accept multipart/form-data
// @Param type query string true "type" (1:单图,2:多图, 3:base64图片)
// @Param file formData file true "file"
// @Success 200 {string} string	"{"code": 200, "message": "添加成功"}"
// @Success 200 {string} string	"{"code": -1, "message": "添加失败"}"
// @Router 你的路径
// @Security Bearer
func (e File) UploadFile(c *gin.Context) {
	e.MakeContext(c)
	tag, _ := c.GetPostForm("type")
	urlPrefix := fmt.Sprintf("http://%s/", c.Request.Host)
	var fileResponse FileResponse

	switch tag {
	case "1": // 单图
		var done bool
		fileResponse, done = e.singleFile(c, fileResponse, urlPrefix)
		if done {
			return
		}
		e.OK(fileResponse, "上传成功")
		return
	case "2": // 多图
		multipartFile := e.multipleFile(c, urlPrefix)
		e.OK(multipartFile, "上传成功")
		return
	case "3": // base64
		fileResponse = e.baseImg(c, fileResponse, urlPrefix)
		e.OK(fileResponse, "上传成功")
	default:
		var done bool
		fileResponse, done = e.singleFile(c, fileResponse, urlPrefix)
		if done {
			return
		}
		e.OK(fileResponse, "上传成功")
		return
	}

}

func (e File) baseImg(c *gin.Context, fileResponse FileResponse, urlPerfix string) FileResponse {
	files, _ := c.GetPostForm("file")
	file2list := strings.Split(files, ",")
	ddd, _ := base64.StdEncoding.DecodeString(file2list[1])
	guid := uuid.New().String()
	fileName := guid + ".jpg"
	err := utils.IsNotExistMkDir(path)
	if err != nil {
		e.Error(500, errors.New(""), "初始化文件路径失败")
	}
	base64File := path + fileName
	_ = ioutil.WriteFile(base64File, ddd, 0666)
	typeStr := strings.Replace(strings.Replace(file2list[0], "data:", "", -1), ";base64", "", -1)
	fileResponse = FileResponse{
		Size:     pkg.GetFileSize(base64File),
		Path:     base64File,
		FullPath: urlPerfix + base64File,
		Name:     "",
		Type:     typeStr,
	}
	source, _ := c.GetPostForm("source")
	err = thirdUpload(source, fileName, base64File)
	if err != nil {
		e.Error(200, errors.New(""), "上传第三方失败")
		return fileResponse
	}
	if source != "1" {
		fileResponse.Path = "/static/uploadfile/" + fileName
		fileResponse.FullPath = "/static/uploadfile/" + fileName
	}
	return fileResponse
}

func (e File) multipleFile(c *gin.Context, urlPerfix string) []FileResponse {
	files := c.Request.MultipartForm.File["file"]
	source, _ := c.GetPostForm("source")
	var multipartFile []FileResponse
	for _, f := range files {
		guid := uuid.New().String()
		fileName := guid + "." + f.Filename

		err := utils.IsNotExistMkDir(path)
		if err != nil {
			e.Error(500, errors.New(""), "初始化文件路径失败")
		}
		multipartFileName := path + fileName
		err1 := c.SaveUploadedFile(f, multipartFileName)
		fileType, _ := utils.GetType(multipartFileName)
		if err1 == nil {
			err := thirdUpload(source, fileName, multipartFileName)
			if err != nil {
				e.Error(500, errors.New(""), "上传第三方失败")
			} else {
				fileResponse := FileResponse{
					Size:     pkg.GetFileSize(multipartFileName),
					Path:     multipartFileName,
					FullPath: urlPerfix + multipartFileName,
					Name:     f.Filename,
					Type:     fileType,
				}
				if source != "1" {
					fileResponse.Path = "/static/uploadfile/" + fileName
					fileResponse.FullPath = "/static/uploadfile/" + fileName
				}
				multipartFile = append(multipartFile, fileResponse)
			}
		}
	}
	return multipartFile
}

func (e File) singleFile(c *gin.Context, fileResponse FileResponse, urlPerfix string) (FileResponse, bool) {
	files, err := c.FormFile("file")

	if err != nil {
		e.Error(200, errors.New(""), "图片不能为空")
		return FileResponse{}, true
	}
	// 上传文件至指定目录
	guid := uuid.New().String()

	fileName := guid + "." + files.Filename

	err = utils.IsNotExistMkDir(path)
	if err != nil {
		e.Error(500, errors.New(""), "初始化文件路径失败")
	}
	singleFile := path + fileName
	_ = c.SaveUploadedFile(files, singleFile)
	fileType, _ := utils.GetType(singleFile)
	fileResponse = FileResponse{
		Size:     pkg.GetFileSize(singleFile),
		Path:     singleFile,
		FullPath: urlPerfix + singleFile,
		Name:     files.Filename,
		Type:     fileType,
	}
	//source, _ := c.GetPostForm("source")
	//err = thirdUpload(source, fileName, singleFile)
	//if err != nil {
	//	e.Error(200, errors.New(""), "上传第三方失败")
	//	return FileResponse{}, true
	//}
	fileResponse.Path = "/static/uploadfile/" + fileName
	fileResponse.FullPath = "/static/uploadfile/" + fileName
	return fileResponse, false
}

func thirdUpload(source string, name string, path string) error {
	switch source {
	case "2":
		return ossUpload("img/"+name, path)
	case "3":
		return qiniuUpload("img/"+name, path)
	}
	return nil
}

func ossUpload(name string, path string) error {
	oss := file_store.ALiYunOSS{}
	return oss.UpLoad(name, path)
}

func qiniuUpload(name string, path string) error {
	oss := file_store.ALiYunOSS{}
	return oss.UpLoad(name, path)
}

2 将文件地址名称存入对应数据库库,修改字段

  • dto中

需要对收到的结构体修改,留出文件和文件操作的字段

type ReportGetPageReq struct {
	//省略不必要字段....
	FilesOpt  string   `json:"filesOpt" comment:"文件操作"`
	Files     []string `json:"files" comment:"报告文件"`
}

func (s *ReportGetPageReq) GenerateNoneFile(model *model.Report) {
    // 不要写 model.files = s.files
    // 因为s的files是字符串数组,而model的files是字符串,无法相等

}

type InnerFile struct {
	FileName string `json:"FileName"`
	FilePath string `json:"FilePath"`
}

//newInnerFiles这个函数生成了文件这个结构体?(文件+路径)

func newInnerFiles(files ...string) []*InnerFile {
	res := make([]*InnerFile, 0, len(files))
	for _, f := range files {
		tmp := strings.Split(f, "/")
		fmt.Println(tmp)
		fn := strings.Join(strings.Split(tmp[len(tmp)-1], ".")[1:], ".")
		fmt.Println(fn)

		res = append(res, &InnerFile{
			FileName: fn,
			FilePath: path.Join(my_config.CurrentPatentConfig.FileUrl, f),
		})
	}
	return res
}

//生成结构体并添加文件or在原有文件切片后添加文件

func (s *ReportGetPageReq) GenerateAndAddFiles(model *model.Report) {
	s.Generate(model)
	if len(model.Files) == 0 {
		innerFiles := newInnerFiles(s.Files...)
		fbs, _ := json.Marshal(innerFiles)
		// returns the JSON encoding of v 输入v,遍历v,返回byte[]
		model.Files = string(fbs)
	} else { // 长度大于0,append,不是大于1
		files := make([]*InnerFile, 0)
		_ = json.Unmarshal([]byte(model.Files), &files)
		// Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v.
		innerFiles := newInnerFiles(s.Files...)
		// 以下步骤相同
		innerFiles = append(innerFiles, files...)
		fbs, _ := json.Marshal(innerFiles)
		model.Files = string(fbs)
	}
}

```go
//生成结构体,接住删除 *部分文件* 后的files
func (s *ReportGetPageReq) GenerateAndDeleteFiles(model *model.Report) {
//我们需要根据s.Files去删除数据库中(model.Files)里的文件,所以如果s.Files空,就不会删除文件!
//此处是全删,就是把model.Files整个放到s.Files里,也可以选择部分删除
//需要注意s.Files是string的切片,model.Files只是string,需要Unmarshal格式化为结构体struct,而且元素为FilePath
	s.Generate(model)
	if len(model.Files) != 0 {
		files := make([]*InnerFile, 0)
		_ = json.Unmarshal([]byte(model.Files), &files)
		// 把原始文件值存在&files里(Unmarshal函数做的数据结构很好看)
		needToDel := make(map[string]struct{})
		//files[i]的FilePath就是s.Files的元素,手动格式化s.Files
		s.Files = []string{}
		for i, _ := range files {
			s.Files = append(s.Files, files[i].FilePath)
			//学习此处切片append的go的用法
		}

		for _, df := range s.Files {
			// 遍历s.Files,写入needToDel
			needToDel[df] = struct{}{}
			fmt.Println("needToDel", needToDel) //如果这里不打印,没有进来,就因为s.Files是空的 √
		}
		slow := 0
		for _, f := range files {
			// 此处files是unmarshal来的原始切片,判断 key 是否在 map 里 if _, ok := map[key];
			// ok 是 true 则 正是needToDel的元素;
			// ok 是 false 则 slow++,此时该元素f要保留,所以需要在files里面写入f
			if _, ok := needToDel[f.FilePath]; !ok {
				fmt.Println(needToDel[f.FilePath])
				files[slow] = f
				slow++
			}
		}
		files = files[:slow] //截取从头到slow的切片,不包括下标slow
		fmt.Println(&files)
		fbs, _ := json.Marshal(files)
		model.Files = string(fbs)
	}
}
  • 在删除的过程中出现数据库未更新的情况,debug后发现是数据结构不匹配
  • 参考数据结构如下

在这里插入图片描述说过经过json.Unmarshal的model.Files数据结构 files 非常好看,如下在这里插入图片描述

  • files作为指针切片,可以用files[i].FilePath访问

报错

  1. Proxy error: Could not proxy request /login from
  • vue.config.js proxy地址检查,即使写了部署的服务器的地址,也要ping的通才能打开
  1. runtime error: index out of range [0] with length 0
  • 大部分情况就是没有初始化数组就直接用了,比如list[0]但是没写make语句,举例
list := make([]model.Report, 0)

或者

req1.ReportIds = make([]int, len(list))
  1. request body not present anymore
  • 一个api进行两次查询使用不同的req结构体时,需要重新绑定req并且排查err
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值