一、日常下载图片到本地

//下载文件

func downloadfile(url, filename string) {

r, err := http.Get(url)

if err != nil {

fmt.Println("err", err.Error())

}

defer r.Body.Close()

f, err := os.Create(filename)

if err != nil {

fmt.Println("err", err.Error())

}

defer f.Close()

n, err := io.Copy(f, r.Body)

fmt.Println(n, err)

}

func main() {

var url = "https://img-s-msn-com.akamaized.net/tenant/amp/entityid/AA1jjNfg.img?w=1920&h=1080&q=60&m=2&f=jpg"

downloadfile(url, "test.jpg")

}

这里以一张图片为例子

从后台可以看到图片的url地址

修改main函数中的url地址,可以下载到本地

结果如下

二、显示文件下载进度

package main

import (

"fmt"

"io"

"net/http"

"os"

)

func downloadFile(url, filename string) {

r, err := http.Get(url)

if err != nil {

panic(err)

}

defer func() {_ = r.Body.Close()}()

f, err := os.Create(filename)

if err != nil {

panic(err)

}

defer func() {_ = f.Close()}()

n, err := io.Copy(f, r.Body)

fmt.Println(n, err)

}

type Reader struct {

io.Reader

Total int64

Current int64

}

func (r *Reader) Read(p []byte) (n int, err error){

n, err = r.Reader.Read(p)

r.Current += int64(n)

fmt.Printf("\r进度 %.2f%%", float64(r.Current * 10000/ r.Total)/100)

return

}

func DownloadFileProgress(url, filename string) {

r, err := http.Get(url)

if err != nil {

panic(err)

}

defer func() {_ = r.Body.Close()}()

f, err := os.Create(filename)

if err != nil {

panic(err)

}

defer func() {_ = f.Close()}()

reader := &Reader{

Reader: r.Body,

Total: r.ContentLength,

}

_, _ = io.Copy(f, reader)

}

func main() {

// 自动文件下载,比如自动下载图片、压缩包

url := "https://user-gold-cdn.xitu.io/2019/6/30/16ba8cb6465a6418?w=826&h=782&f=png&s=279620"

filename := "poloxue.png"

DownloadFileProgress(url, filename)

}

io.copy函数实现原理

func Copy(dst Writer, src Reader) (written int64, err error) {

// Copy 函数 调用了 copyBuffer 函数来实现

return copyBuffer(dst, src, nil)

}

func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {

// 如果 源Reader 实现了 WriterTo 接口,直接调用该方法 将数据写入到 目标Writer 当中

if wt, ok := src.(WriterTo); ok {

return wt.WriteTo(dst)

}

// 同理,如果 目标Writer 实现了 ReaderFrom 接口,直接调用ReadFrom方法

if rt, ok := dst.(ReaderFrom); ok {

return rt.ReadFrom(src)

}

// 如果没有传入缓冲区,此时默认 创建一个 缓冲区

if buf == nil {

// 默认缓冲区 大小为 32kb

size := 32 * 1024

// 如果源Reader 为LimitedReader, 此时比较 可读数据数 和 默认缓冲区,取较小那个

if l, ok := src.(*LimitedReader); ok && int64(size) > l.N {

if l.N < 1 {

size = 1

} else {

size = int(l.N)

}

}

buf = make([]byte, size)

}

for {

// 调用Read方法 读取数据

nr, er := src.Read(buf)

if nr > 0 {

// 将数据写入到 目标Writer 当中

nw, ew := dst.Write(buf[0:nr])

// 判断写入是否 出现了 错误

if nw < 0 || nr < nw {

nw = 0

if ew == nil {

ew = errInvalidWrite

}

}

// 累加 总写入数据

written += int64(nw)

if ew != nil {

err = ew

break

}

// 写入字节数 小于 读取字节数,此时报错

if nr != nw {

err = ErrShortWrite

break

}

}

if er != nil {

if er != EOF {

err = er

}

break

}

}

return written, err

}func Copy(dst Writer, src Reader) (written int64, err error) {

// Copy 函数 调用了 copyBuffer 函数来实现

return copyBuffer(dst, src, nil)

}

func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {

// 如果 源Reader 实现了 WriterTo 接口,直接调用该方法 将数据写入到 目标Writer 当中

if wt, ok := src.(WriterTo); ok {

return wt.WriteTo(dst)

}

// 同理,如果 目标Writer 实现了 ReaderFrom 接口,直接调用ReadFrom方法

if rt, ok := dst.(ReaderFrom); ok {

return rt.ReadFrom(src)

}

// 如果没有传入缓冲区,此时默认 创建一个 缓冲区

if buf == nil {

// 默认缓冲区 大小为 32kb

size := 32 * 1024

// 如果源Reader 为LimitedReader, 此时比较 可读数据数 和 默认缓冲区,取较小那个

if l, ok := src.(*LimitedReader); ok && int64(size) > l.N {

if l.N < 1 {

size = 1

} else {

size = int(l.N)

}

}

buf = make([]byte, size)

}

for {

// 调用Read方法 读取数据

nr, er := src.Read(buf)

if nr > 0 {

// 将数据写入到 目标Writer 当中

nw, ew := dst.Write(buf[0:nr])

// 判断写入是否 出现了 错误

if nw < 0 || nr < nw {

nw = 0

if ew == nil {

ew = errInvalidWrite

}

}

// 累加 总写入数据

written += int64(nw)

if ew != nil {

err = ew

break

}

// 写入字节数 小于 读取字节数,此时报错

if nr != nw {

err = ErrShortWrite

break

}

}

if er != nil {

if er != EOF {

err = er

}

break

}

}

return written, err

}

从中可以看出,io.copy函数是通过read读取文件进行写入新创建的文件,因此,重写后的read函数除了实现原来的read功能,还增加了进度条功能。

文章链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: