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(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。