Golang实现异步上传文件支持进度条查询的方法
业务背景
业务需求要求开发一个异步上传文件的接口,并支持上传进度的查询。
需求分析
ZIP压缩包中,包含一个csv文件和一个图片文件夹,要求:解析csv数据存入mongo,将图片文件夹中的图片信息对应上csv中的人员信息。
ZIP压缩包解压
使用golang自带的 "archive/zip"包解压。
funcdecompressZip(filePath,deststring)(string,string,error){ varcsvNamestring imageFolder:=path.Base(filePath) ext:=path.Ext(filePath) folderName:=strings.TrimSuffix(imageFolder,ext) src,err:=os.Open(filePath) iferr!=nil{ return"","",err } defersrc.Close() zipFile,err:=zip.OpenReader(src.Name()) iferr!=nil{ return"","",err } deferzipFile.Close() err=os.MkdirAll(path.Join(dest,folderName),os.ModePerm) for_,innerFile:=rangezipFile.File{ info:=innerFile.FileInfo() ifinfo.IsDir(){ continue } dst,err:=os.Create(path.Join(dest,folderName,info.Name())) iferr!=nil{ fmt.Println(err.Error()) continue } src,err:=innerFile.Open() iferr!=nil{ fmt.Println(err.Error()) continue } io.Copy(dst,src) } destPath,err:=ioutil.ReadDir(path.Join(dest,folderName)) iferr!=nil{ return"","",err } for_,v:=rangedestPath{ ifpath.Ext(v.Name())==".csv"{ csvName=path.Join(dest,folderName,v.Name()) } } returnfolderName,csvName,nil }
在这个解压的过程中,压缩包的树结构只能到2层
import.zip ┝┅┅import.csv ┖┅┅images
在解压后,所有的文件都会在同一个目录下,既images中的图片会变成和.csv文件同级
验证csv文件编码格式是否为UTF-8
funcValidUTF8(buf[]byte)bool{ nBytes:=0 fori:=0;i6{//因为UTF8编码单字符最多不超过6个字节 returnfalse } nBytes--//减掉首字节的一个计数 } }else{//处理多字节字符 ifbuf[i]&0xc0!=0x80{//判断多字节后面的字节是否是10开头 returnfalse } nBytes-- } } returnnBytes==0 }
后续支持utf-8转码
这个utf8编码判断方法是网上down下来的,后续优化一下
主逻辑
typeLineWrongstruct{ LineNumberint64`json:"line_number"` Msgstring`json:"msg"` } funcImport(/*自定义参数*/){ //decompresszipfiletodestinationaddress folder,csvName,err:=Decompress(path.Join(constant.FolderPrefix,req.FilePath),dest) iferr!=nil{ fmt.Println(err.Error()) } //checkifthefileencodingisutf8 b,err:=ioutil.ReadFile(csvName) iferr!=nil{ fmt.Println(err.Error()) } if!utils.ValidUTF8(b){ fmt.Println(errors.New("数据编码错误,请使用utf-8格式csv!")) } //creategoroutinetoanalysisdataintomongodb varwgsync.WaitGroup wg.Add(1) //usedtointerruptgoroutine resultChan:=make(chanerror) //usedtorecordwrongrowincsv lW:=make(chan[]LineWrong) gofunc(ctx*gin.Context,Name,csvPath,dir,folderstring){ deferwg.Done() tidT,ciT,lwT,err:=importCsv(ctx,Name,csvPath,dir,folder) resultChan<-err iferr!=nil{ fmt.Println(err.Error()) } lW<-lwT iflen(lwT)==0{ importClassData(ctx,tidT,ciT) } }(ctx,req.Name,csvName,dest,folder) err=<-resultChan lineWrong:=<-lW close(lW) ··· } //pre-analysisdataincsvandthroughwrongdatawithlinenumbersandinformation funcimportCsv()(){ ··· } //analysisdataagainandsavedataintomongodb,ifisthereanyerror,throughthemsameasimport() funcimportClassData()(){ ··· conn,err:=connect() iferr!=nil{ returnerr } deferconn.Close() conn.Do("hset",taskId,"task_id",(curLine*100)/totalLines) ··· }
将错误信息以channel接收,使用 package"sync"的 sync.WaitGroup控制异步协程。在入库的过程中,将当前的进度存入redis。
查询进度接口
funcQueryImport()(){ conn,err:=connect() iferr!=nil{ returnnil,err } deferconn.Close() progress,_:=conn.Do("hget",key,field) ifpro,ok:=progress.([]uint8);ok{ ba:=[]byte{} for_,b:=rangepro{ ba=append(ba,byte(b)) } progress,_=strconv.Atoi(string(ba)) } returnprogress }
从redis中取出来的数据是[]uint8类型数据,先断言,然后转类型返回。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。