用 wails 包装web 文件上传的问题

用 wails 包装web 文件上传的问题

问题发现

wails 包装了现有的web 项目,在测试的时候发现,文件上传的时候,上传失败。看了下请求,文件传了,没问题。

难道是有鬼了!!!

启动后台,打断点继续找问题,发现后端根本没收到文件,凭空消失了吗?

寻找问题

百度感觉是解决不了啥问题,然后就在github 上找到wails ,根据问题的表现,用Ai 翻译了下,提了个issues

github.com/wailsapp/wa...

css 复制代码
Description
I used the AccessServer's handler to receive requests from the frontend, but I found that the ContentLength of the received POST request is always 0, and there is no data in the body either.

前端通过 表单上传了一个文件,但是后台无法接受文件

To Reproduce
I created a form in the frontend to upload files, but the backend is not receiving the files.

前端通过 表单上传了一个文件,但是后台无法接受文件

Expected behaviour
can upload file

能上传文件

Screenshots

问题是下午5点钟提上去的,结果 5点26的时候就得到回复,这个反馈速度还是很快的。回答的大致意思就是,这个是webview2 的一个bug ,然后给了个链接,过去看了下,确实如此

MicrosoftEdge/WebView2Feedback#2162

这个bug 已经有很久了 commented on Feb 11, 2022 ,到现在依旧没解决,有兴趣的可以点进去看下。

wails 那边回复,换种思路,用go的方式解决。

解决问题

用go 的方式解决,因为项目是最先开发的web 段,用的elementui ,上传组件是封装好的。解决方案要求要改动最小,然后就自己写了一个同名的上传组件,兼容elementui 的上传组件,在包装的这边,替换上就行了,说干就干。

go的上传逻辑如下 ,主要就是把 upload 组件的 选择文件 ,和上传文件的两个步骤,用go来实现,其他的保持不变

代码实现

1,先做下go打开文件选择框,和上传文件的逻辑

go 复制代码
// 打开选择文件对话框
func (a *App) OpenDialog() string {

	// 返回选择的文件

	options := runtime.OpenDialogOptions{
		// Filters: ,
	}

	filepath, err := runtime.OpenFileDialog(a.ctx, options)

	if err != nil {
		return "选择文件失败"
	}

	return filepath

}
go 复制代码
// 上传文件的方法,传入文件路径
func (a *App) UploadFile(upInfo UpdateInfo) UpdateResp {

	// fmt.Println("upinfo", upInfo)

	file, err := os.OpenFile(upInfo.Filename, os.O_RDONLY, 0)
	if err != nil {
		fmt.Println(err.Error() + upInfo.Filename)
		return UpdateResp{400, err.Error()}
	}

	body := new(bytes.Buffer)

	// 创建一个 `Writer` 对象,并将其写入到 `w` 中。
	w := multipart.NewWriter(body)

	// 将 `file` 对象写入到 `Writer` 中。
	// 注意,这里的 `file` 对象必须实现 `io.Reader` 接口。

	fileForm, err := w.CreateFormFile("file", upInfo.Filename)
	if err != nil {
		return UpdateResp{400, err.Error()}
	}

	_, err = io.Copy(fileForm, file)
	if err != nil {
		return UpdateResp{400, err.Error()}
	}
	// 关闭 `Writer` 对象。
	err = w.Close()
	if err != nil {
		return UpdateResp{400, err.Error()}
	}

	// 发送 `POST` 请求。
	req, err := http.NewRequest("POST", BaseUrl+upInfo.Action, body)
	if err != nil {
		return UpdateResp{400, err.Error()}
	}

	// 设置请求头。
	req.Header.Set("Content-Type", w.FormDataContentType())
	req.Header.Set("Authorization", upInfo.Header["Authorization"])

	// 发送请求。
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return UpdateResp{400, err.Error()}
	}

	// 关闭响应。
	defer resp.Body.Close()

	// 检查响应状态码。
	if resp.StatusCode != http.StatusOK {
		return UpdateResp{400, "上传文件失败"}
	}

	respBy, _ := io.ReadAll(resp.Body)

	// 返回 nil。
	return UpdateResp{200, string(respBy)}
}

2,写一个自定义的upload 组件,把UploadFile , OpenDialog 引进来,点击上传组件的时候,调用OpenDialog

vue 复制代码
<template>
  <div @click="openChoseFileDlg">
    <slot></slot>
  </div>
</template>
<script>
import { getToken } from "@/utils/auth";
import { UploadFile,OpenDialog} from "@w/go/api/App";

export default {
  name:"ElUploadCs",
  props: {
    action: {
      type: String,
    },
    accept: {
      type: String,
    },
    beforeUpload:{
      type:Function,
      default:()=>{

      }
    },
    onSuccess:{
      type:Function,
      default:()=>{

      }
    },
    onError:{
      type:Function,
      default:()=>{

      }
    },
    onProgress:{
      type:Function,
      default:()=>{

      }
    },
  },
  data() {
    return {
      headerMsg: { Authorization: getToken() },
      filename:"",
    };
  },
  methods: {

    openChoseFileDlg(){
      OpenDialog().then(res=>{
        console.log("chose file :",res)
        if (res!=""){
          this.filename = res
          this.doUploadFile()
        }
      })
    },

    doUploadFile(){
      let params = {
        "action":this.action,
        "filename":this.filename,
        "header":this.headerMsg
      }
      console.log(params)
      this.beforeUpload()
      this.handleProgress()
      UploadFile(params).then(res=>{
          console.log(res)
          if (res.code== 200) {
            let resData = JSON.parse(res.data)
            console.log(resData)
            this.onSuccess(resData)
          }else{
            this.onError()
          }

          // this.$emit("onSuccess",resData)
      })

    },

    handleProgress() {
      if (this.utips) {
        this.dialogViable = false;
        this.fileUploadVisible = true;
      }
    },
    handleError() {
      if (this.utips) {
        this.fileUploadVisible = false;
        this.vMsg = "文件导入失败!";
        this.fileSuccessVisible = true;
      }
    },
  },
};
</script>

3,调用,引入自定义组件 ,命名为 ElUplaod,跟element-ui 里面的组件名字一样,然后在 components 中引入就可以了

javascript 复制代码
import ElUpload from "@/components/upload-cs"

components: {ElUpload },
相关推荐
程序员爱技术1 小时前
Vue 2 + JavaScript + vue-count-to 集成案例
前端·javascript·vue.js
并不会2 小时前
常见 CSS 选择器用法
前端·css·学习·html·前端开发·css选择器
衣乌安、2 小时前
【CSS】居中样式
前端·css·css3
兔老大的胡萝卜2 小时前
ppk谈JavaScript,悟透JavaScript,精通CSS高级Web,JavaScript DOM编程艺术,高性能JavaScript pdf
前端·javascript
低代码布道师2 小时前
CSS的三个重点
前端·css
耶啵奶膘4 小时前
uniapp-是否删除
linux·前端·uni-app
王哈哈^_^5 小时前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
幼儿园老大*6 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
cs_dn_Jie6 小时前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic6 小时前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js