go 从url下载大文件(支持断点续传)


package main

import (
	"bufio"
	"errors"
	"fmt"
	"github.com/go-emix/utils"
	"io"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"time"
)

func down(ur, dir, fn string, to time.Duration, onErr func(err error)) {
	if !utils.FileIsExist(dir) {
		err := os.MkdirAll(dir, os.ModePerm)
		if err != nil {
			fmt.Println("mkdir "+dir+" err:", err.Error())
			onErr(err)
			return
		}
	}
	dfn := dir + "/" + fn
	var file *os.File
	var size int64
	if utils.FileIsExist(dfn) {
		fi, err := os.OpenFile(dfn, os.O_RDWR, os.ModePerm)
		if err != nil {
			fmt.Println(fn, "open err:", err)
			onErr(err)
			return
		}
		stat, _ := fi.Stat()
		size = stat.Size()
		sk, err := fi.Seek(size, 0)
		if err != nil {
			fmt.Println(fn, "seek err:", err)
			_ = fi.Close()
			onErr(err)
			return
		}
		if sk != size {
			fmt.Printf("%s seek length not equal file size,"+
				"seek=%d,size=%d\n", fn, sk, size)
			_ = fi.Close()
			onErr(errors.New("seek length not equal file size"))
			return
		}
		file = fi
	} else {
		create, err := os.Create(dfn)
		if err != nil {
			fmt.Println(fn, "create err:", err)
			onErr(err)
			return
		}
		file = create
	}
	client := &http.Client{}
	client.Timeout = to
	request := http.Request{}
	request.Method = http.MethodGet
	if size != 0 {
		header := http.Header{}
		header.Set("Range", "bytes="+strconv.FormatInt(size, 10)+"-")
		request.Header = header
	}
	parse, err := url.Parse(ur)
	if err != nil {
		fmt.Println(ur, "url err:", err)
		onErr(err)
		return
	}
	request.URL = parse
	get, err := client.Do(&request)
	if err != nil {
		fmt.Println(ur, "get err:", err)
		onErr(err)
		return
	}
	defer func() {
		err := get.Body.Close()
		if err != nil {
			fmt.Println(fn, "body close:", err.Error())
			onErr(err)
		}
		err = file.Close()
		if err != nil {
			fmt.Println(fn, "file close:", err.Error())
			onErr(err)
		}
	}()
	if get.ContentLength == 0 {
		fmt.Println(fn, "already downloaded")
		return
	}
	body := get.Body
	writer := bufio.NewWriter(file)
	bs := make([]byte, 10*1024*1024) //每次读取的最大字节数,不可为0
	for {
		var read int
		read, err = body.Read(bs)
		if err != nil {
			if err != io.EOF {
				fmt.Println(fn, "read err:"+err.Error())
				onErr(err)
			} else {
				err = nil
			}
			break
		}
		_, err = writer.Write(bs[:read])
		if err != nil {
			fmt.Println(fn, "write err:"+err.Error())
			onErr(err)
			break
		}
	}
	if err != nil {
		return
	}
	err = writer.Flush()
	if err != nil {
		fmt.Println(fn, "writer flush:", err.Error())
		onErr(err)
		return
	}
	fmt.Println(fn, "download success")
}

go test

 

package main

import (
	"fmt"
	"testing"
	"time"
)

func TestDown(t *testing.T) {
	//ur := "https://dl.google.com/go/go1.15.2.linux-amd64.tar.gz"
	ur := "https://dl.google.com/go/go1.15.2.src.tar.gz"
	fn := "src.7z"
	//fn := "linux.7z"
	go down(ur, "download", fn, time.Hour, func(err error) {

	})
    //打点计时器,监听显示
	tick := time.Tick(time.Minute)
	for range tick {
		fmt.Println("...")
	}
}

ps : 不需先创建目录,会自动创建;onErr是错误回调函数

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
在Unity 2022中实现文件下载支持断点续传,同时支持GUI进度显示可以使用UnityWebRequest来实现。以下是一个简单的示例: ``` public class DownloadManager : MonoBehaviour { private UnityWebRequest request; private string savePath; private long fileSize; private long bytesDownloaded; private bool isDownloading; // GUI进度条相关 private float progress; private Rect progressRect = new Rect(50, 50, 200, 20); public void Download(string url, string savePath) { if (isDownloading) return; this.savePath = savePath; request = UnityWebRequest.Head(url); request.SendWebRequest(); StartCoroutine(GetFileSize()); } IEnumerator GetFileSize() { while (!request.isDone) yield return null; if (request.result != UnityWebRequest.Result.Success) { Debug.LogError("获取文件大小失败!"); yield break; } fileSize = long.Parse(request.GetResponseHeader("Content-Length")); if (File.Exists(savePath)) { FileInfo fileInfo = new FileInfo(savePath); bytesDownloaded = fileInfo.Length; request = UnityWebRequest.Get(request.uri.ToString()); request.SetRequestHeader("Range", "bytes=" + bytesDownloaded + "-"); } else { request = UnityWebRequest.Get(request.uri.ToString()); } request.downloadHandler = new DownloadHandlerBuffer(); yield return request.SendWebRequest(); if (request.result != UnityWebRequest.Result.Success) { Debug.LogError("下载文件失败!"); yield break; } isDownloading = true; // 启动协程来写入文件 StartCoroutine(WriteToFile()); } IEnumerator WriteToFile() { using (FileStream fs = new FileStream(savePath, FileMode.Append)) { byte[] data = request.downloadHandler.data; fs.Write(data, 0, data.Length); bytesDownloaded += data.Length; while (isDownloading) { yield return new WaitForSeconds(0.1f); data = request.downloadHandler.data; if (data != null && data.Length > 0) { fs.Write(data, 0, data.Length); bytesDownloaded += data.Length; } progress = (float)bytesDownloaded / fileSize; if (bytesDownloaded >= fileSize) { isDownloading = false; Debug.Log("文件下载完成!"); yield break; } } } } void OnGUI() { GUI.HorizontalScrollbar(progressRect, 0, progress, 0, 1); } } ``` 在这个示例中,我们首先通过发送HEAD请求来获取文件大小,然后设置Range头字段来支持断点续传。我们使用了DownloadHandlerBuffer来下载文件,并使用FileStream来将下载的数据写入文件。我们还使用了一个协程来检查下载进度,并在GUI上显示进度条。如果下载完成,则停止显示进度条。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值