Go實(shí)現(xiàn)文件分片上傳_第1頁
Go實(shí)現(xiàn)文件分片上傳_第2頁
Go實(shí)現(xiàn)文件分片上傳_第3頁
Go實(shí)現(xiàn)文件分片上傳_第4頁
Go實(shí)現(xiàn)文件分片上傳_第5頁
已閱讀5頁,還剩5頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

第Go實(shí)現(xiàn)文件分片上傳Go語言在寫HTTP服務(wù)程序時(shí),會(huì)經(jīng)常用到文件上傳和文件下載,文件上傳和文件下載都可以用http包,默認(rèn)的功能基本上夠用了。http包支持文件下載的斷點(diǎn)續(xù)傳和進(jìn)度顯示,文件上傳貌似不支持?jǐn)帱c(diǎn)續(xù)傳,不知道是不是要web端來實(shí)現(xiàn)。

然后我自己配合web端實(shí)現(xiàn)了大文件分片上傳,來完成斷點(diǎn)續(xù)傳和進(jìn)度顯示的功能。

基本思想是,在web端將上傳的文件進(jìn)行分片處理,然后向服務(wù)端發(fā)送上傳請(qǐng)求(UploadRequest)包括文件名,MD5,文件大小,和文件總片數(shù)。

然后開始一片一片的上傳(Upload)。如果暫停了或者斷網(wǎng)失敗了,就記錄當(dāng)前上傳片的片數(shù),下一次上傳,重發(fā)UploadRequest,chunkPos為續(xù)傳位置片數(shù),然后從這個(gè)記錄的片數(shù)開始接著上傳(Upload)。在服務(wù)端,記錄首次請(qǐng)求的文件名,MD5,文件大小,文件總片數(shù),然后開始接收每一片,將每一個(gè)片保存為一個(gè)單獨(dú)的文件,在接收完最后一個(gè)片時(shí),將所有的分片份文件進(jìn)行合并。保存為對(duì)應(yīng)的文件名。最后再校驗(yàn)MD5碼。

對(duì)于不同用戶的請(qǐng)求,將文件上傳的信息存儲(chǔ)到session中,當(dāng)前文件上傳暫停后,上傳其它文件時(shí),向session增加新文件的信息。暫停后,接著上傳最開始的文件,則直接從session中取得該文件的信息,包括文件名,上傳到某一個(gè)片的信息等。

服務(wù)端實(shí)現(xiàn)代碼:

funcUploadRequest(whttp.ResponseWriter,r*http.Request){

log.WithFields(log.Fields{

"HTTP":r.Method,

"FUNC":"UploadRequest",

}).Info("HTTPREQUEST")

header:=w.Header()

header.Add("Content-Type","application/json")

/*sessionauthentication*/

sess:=session.GlobalSessions.SessionCheck(w,r)

ifsess==nil{

log.Error("sesscheckerror")

fmt.Fprintf(w,config.FmtStr,"3000","error","sessioncheckerror")

return

}

deferr.Body.Close()

con,_:=ioutil.ReadAll(r.Body)//獲取post的body數(shù)據(jù)

log.Info("UploadRequestjson:",string(con))

varuploadRequploadRequest

err:=json.Unmarshal([]byte(con),uploadReq)/*解析json字符串?dāng)?shù)據(jù)到結(jié)構(gòu)體中*/

iferr!=nil{

log.Error("jsonunmarshalerror")

fmt.Fprintf(w,config.FmtStr,"2000","error","jsonunmarshalerror")

return

}

switchuploadReq.Option{

case"reUploadFile":

{

sess.Set("currentFile",uploadReq)

err:=os.Remove("./tmp/"+uploadReq.FileName+"/"+uploadReq.FileName+"_"+uploadReq.ChunkPos)

iferr!=nil{

log.Error(err)

}

fmt.Fprintf(w,config.FmtStr,"1000","success","reuploadrequestsuccess")

}

case"uploadFile":

{

err=os.Mkdir("./tmp/"+uploadReq.FileName,0777)

iferr!=nil{

log.Info(err)

fmt.Fprintf(w,config.FmtStr,"3000","error","mkdirerror")

}else{

log.Infof("createdir%sok\r\n",uploadReq.FileName)

}

//文件上傳信息保存,保存到session中,用于分片續(xù)傳時(shí)使用

sess.Set(uploadReq.FileName,uploadReq)

sess.Set("currentFile",uploadReq)

fmt.Fprintf(w,config.FmtStr,"1000","success","uploadrequestsuccess")

}

case"uploadCancel":

{

err=os.RemoveAll("./tmp/"+uploadReq.FileName)

iferr!=nil{

log.Fatal(err)

}

sess.Delete(uploadReq.FileName)

fmt.Fprintf(w,config.FmtStr,"1000","success","uploadfilesuccess")

return

}

default:

{

fmt.Fprintf(w,config.FmtStr,"2000","error","uploadrequestoptionerror")

}

}

/*分片上傳功能實(shí)現(xiàn)*/

funcUpload(whttp.ResponseWriter,r*http.Request){

log.WithFields(log.Fields{

"HTTP":r.Method,

"FUNC":"Upload",

}).Info("HTTPREQUEST")

header:=w.Header()

header.Add("Content-Type","application/json")

/*sessionauthentication*/

sess:=globalSessions.SessionCheck(w,r)//session檢查的接口封裝

ifsess==nil{

log.Error("sessioncheckfailed")

fmt.Fprintf(w,config.FmtStr,"3000","error","sessioncheckfailed")

return

}

current_dir:=sess.Get("current_dir")

log.Info("currentdir:",current_dir)

up_dir:=sess.Get("up_dir")

log.Info("up_dir:",up_dir)

//varcurrent_dirstring="./river/test"

/*表單上傳文件*/

ifr.Method=="GET"{

crutime:=time.Now().Unix()

h:=md5.New()

io.WriteString(h,strconv.FormatInt(crutime,10))

token:=fmt.Sprintf("%x",h.Sum(nil))

t,_:=template.ParseFiles("upload.gtpl")

t.Execute(w,token)

}else{

r.ParseMultipartForm(3220)

file,_,err:=r.FormFile("uploadfile")

iferr!=nil{

log.Error(err)

return

}

deferfile.Close()

varuploadFileInfouploadRequest

uploadFileInfo=sess.Get("currentFile").(uploadRequest)

//fmt.Fprintf(w,"%v",sess.Get("fileName"))

varfileNamestring=uploadFileInfo.FileName+"_"+uploadFileInfo.ChunkPos

/*判斷上傳的文件是否已經(jīng)存在,可能發(fā)生同名的情況*/

log.Debug("fileName:",fileName)

_,err=os.Stat("./tmp/"+uploadFileInfo.FileName+"/"+fileName)

varf*os.File

iferr==nil{

fmt.Fprintf(w,config.FmtStr,"3000","error","fileisexsited")

return

}else{

f,err=os.OpenFile("./tmp/"+uploadFileInfo.FileName+"/"+fileName,os.O_WRONLY|os.O_CREATE,0666)//此處假設(shè)當(dāng)前目錄下已存在test目錄

}

deferf.Close()

iferr!=nil{

log.Error(err)

fmt.Fprintf(w,config.FmtStr,"3000","error","createfileerror")

return

}

io.Copy(f,file)//這里進(jìn)行大文件copy會(huì)導(dǎo)致內(nèi)存占用過大。一段時(shí)候后會(huì)進(jìn)行GC回收

log.Info("copyok")

//判斷是否單個(gè)文件是否上傳完畢,上傳完畢則合并文件到目標(biāo)文件夾

ifuploadFileInfo.ChunkPos==uploadFileInfo.ChunkNum{

f.Close()

_,err=os.Stat(current_dir.(string)+uploadFileInfo.FileName)

varfii*os.File

iferr==nil{

//如果已經(jīng)存在這個(gè)文件了,則在當(dāng)前文件名后增加日期

time:=time.Now().Format("2006-01-02_15-04-05")

log.Debug("Time:",time)

varfileName[]byte=[]byte(current_dir.(string)+uploadFileInfo.FileName)

varfileNameFinal[]byte=fileName

log.Debug("file_name:",string(fileName))

varpreFileName[]byte

varsufFileName[]byte

fori:=len(fileName)-1;ii--{

iffileName[i]=='.'{

preFileName=fileName[:i]//文件名前綴test

sufFileName=fileName[i:]//文件名后綴.txt

fileNameFinal=[]byte(string(preFileName)+"_"+time+string(sufFileName))

log.Info("fileNameFile:",fileNameFinal)

break

}

}

fii,err=os.OpenFile(string(fileNameFinal),os.O_WRONLY|os.O_CREATE,0777)//此處假設(shè)當(dāng)前目錄下已存在test目錄

}else{

fii,err=os.OpenFile(current_dir.(string)+uploadFileInfo.FileName,os.O_CREATE|os.O_WRONLY|os.O_APPEND,os.ModePerm)

}

deferfii.Close()

iferr!=nil{

log.Error(err)

fmt.Fprintf(w,config.FmtStr,"3000","error","Openobjectfileerror")

return

}

index,_:=strconv.Atoi(uploadFileInfo.ChunkNum)

fori:=1;i=index;i++{

f11,err:=os.OpenFile("./tmp/"+uploadFileInfo.FileName+"/"+uploadFileInfo.FileName+"_"+strconv.Itoa(int(i)),os.O_RDONLY,os.ModePerm)

iferr!=nil{

log.Error(err)

fmt.Fprintf(w,config.FmtStr,"3000","error","Openslicefileerror")

return

}

b,err:=ioutil.ReadAll(f11)

iferr!=nil{

log.Error(err)

fmt.Fprintf(w,config.FmtStr,"3000","error","ioutilreadallerror")

return

}

fii.Write(b)

f11.Close()

}

err=os.RemoveAll("./tmp/"+uploadFileInfo.FileName)

iferr!=nil{

log.Error(err)

}

sess.Delete(uploadFileInfo.FileName)

fii.Close()

ifuploadFileInfo.MD5==FileMD5(fii.Name()){

err:=os.Remove(current_dir.(string)+fii.Name())

iferr!=nil{

log.Error(err)

}

fmt.Fprintf(w,config.FmtStr,"3000","erro

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論