Golang通过多部分处理图像并流式传输到Azure

Golang通过多部分处理图像并流式传输到Azure,azure,go,image-uploading,azure-storage-blobs,Azure,Go,Image Uploading,Azure Storage Blobs,在学习golang的过程中,我正在尝试编写一个具有多种图像上传功能的web应用程序 我正在使用Azure Blob存储来存储图像,但无法将图像从多部分请求流式传输到Blob存储 以下是我迄今为止编写的处理程序: func (imgc *ImageController) UploadInstanceImageHandler(w http.ResponseWriter, r *http.Request, p httprouter.Params) { reader, err := r.Multipar

在学习golang的过程中,我正在尝试编写一个具有多种图像上传功能的web应用程序

我正在使用Azure Blob存储来存储图像,但无法将图像从多部分请求流式传输到Blob存储

以下是我迄今为止编写的处理程序:

func (imgc *ImageController) UploadInstanceImageHandler(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
reader, err := r.MultipartReader()

if err != nil {
    http.Error(w, err.Error(), http.StatusInternalServerError)
    return
}

for {
    part, partErr := reader.NextPart()

    // No more parts to process
    if partErr == io.EOF {
        break
    }

    // if part.FileName() is empty, skip this iteration.
    if part.FileName() == "" {
        continue
    }

    // Check file type
    if part.Header["Content-Type"][0] != "image/jpeg" {
        fmt.Printf("\nNot image/jpeg!")
        break
    }

    var read uint64
    fileName := uuid.NewV4().String() + ".jpg"
    buffer := make([]byte, 100000000)

    // Get Size
    for {
        cBytes, err := part.Read(buffer)

        if err == io.EOF {
            fmt.Printf("\nLast buffer read!")
            break
        }

        read = read + uint64(cBytes)
    }

    stream := bytes.NewReader(buffer[0:read])
    err = imgc.blobClient.CreateBlockBlobFromReader(imgc.imageContainer, fileName, read, stream, nil)

    if err != nil {
        fmt.Println(err)
        break
    }
}

w.WriteHeader(http.StatusOK)
}

在我的研究过程中,我通读了使用r.FormFile、ParseMultipartForm,但决定尝试学习如何使用MultiPartReader

我能够上传一个图像到golang后端,并使用MultiPartReader将文件保存到我的机器上

目前,我可以将文件上传到Azure,但最终它们被破坏了。文件大小似乎很合适,但显然有些东西不起作用

我是否误解了如何为CreateBlockBlobFromReader创建io.Reader

非常感谢您的帮助

A可以返回io.EOF和有效的最终读取字节,看起来最终字节(cBytes)没有添加到
read
总字节中。另外,要小心:如果由io.EOF以外的
part.Read(buffer)
返回错误,则读取循环可能不会退出。考虑一下。

CreateBlockBlobFromReader接受一个读卡器,而part是一个读卡器,因此您可以直接传递part


你可能还想考虑到Azure块大小的限制可能比图像小,参见

< P> AS @马克说,你可以使用内容读取到一个字节数组中,下面的代码如下。
import (
   "bytes"
   "io/ioutil"
)

partBytes, _ := ioutil.ReadAll(part)
size := uint64(len(partBytes))
blob := bytes.NewReader(partBytes)
err := blobClient.CreateBlockBlobFromReader(container, fileName, size, blob, nil)
import "encoding/base64"

const BLOB_LENGTH_LIMITS uint64 = 64 * 1024 * 1024

partBytes, _ := ioutil.ReadAll(part)
size := uint64(len(partBytes))
if size <= BLOB_LENGTH_LIMITS {
   blob := bytes.NewReader(partBytes)
   err := blobClient.CreateBlockBlobFromReader(container, fileName, size, blob, nil)
} else {
   // Create an empty blob
   blobClient.CreateBlockBlob(container, fileName)
   // Create a block list, and upload each block
   length := size / BLOB_LENGTH_LIMITS
   if length%limits != 0 {
       length = length + 1
   }
   blocks := make([]Block, length)
   for i := uint64(0); i < length; i++ {
        start := i * BLOB_LENGTH_LIMITS
        end := (i+1) * BLOB_LENGTH_LIMITS
        if end > size {
            end = size
        }
        chunk := partBytes[start: end]
        blockId := base64.StdEncoding.EncodeToString(chunk)
        block := Block{blockId, storage.BlockStatusCommitted}
        blocks[i] = block
        err = blobClient.PutBlock(container, fileName, blockID, chunk)
        if err != nil {
        .......
        }
   }
   err = blobClient.PutBlockList(container, fileName, blocks)
   if err != nil {
      .......
   }
}
根据godoc,如下所示

API拒绝大小>64 MiB的请求(但SDK不检查此限制)。要编写更大的blob,请使用、PutBlock和PutBlockList

因此,如果大小大于64MB,代码应该如下所示

import (
   "bytes"
   "io/ioutil"
)

partBytes, _ := ioutil.ReadAll(part)
size := uint64(len(partBytes))
blob := bytes.NewReader(partBytes)
err := blobClient.CreateBlockBlobFromReader(container, fileName, size, blob, nil)
import "encoding/base64"

const BLOB_LENGTH_LIMITS uint64 = 64 * 1024 * 1024

partBytes, _ := ioutil.ReadAll(part)
size := uint64(len(partBytes))
if size <= BLOB_LENGTH_LIMITS {
   blob := bytes.NewReader(partBytes)
   err := blobClient.CreateBlockBlobFromReader(container, fileName, size, blob, nil)
} else {
   // Create an empty blob
   blobClient.CreateBlockBlob(container, fileName)
   // Create a block list, and upload each block
   length := size / BLOB_LENGTH_LIMITS
   if length%limits != 0 {
       length = length + 1
   }
   blocks := make([]Block, length)
   for i := uint64(0); i < length; i++ {
        start := i * BLOB_LENGTH_LIMITS
        end := (i+1) * BLOB_LENGTH_LIMITS
        if end > size {
            end = size
        }
        chunk := partBytes[start: end]
        blockId := base64.StdEncoding.EncodeToString(chunk)
        block := Block{blockId, storage.BlockStatusCommitted}
        blocks[i] = block
        err = blobClient.PutBlock(container, fileName, blockID, chunk)
        if err != nil {
        .......
        }
   }
   err = blobClient.PutBlockList(container, fileName, blocks)
   if err != nil {
      .......
   }
}
导入“编码/base64”
常量块长度限制uint64=64*1024*1024
partBytes,Util:=ioutil.ReadAll(部分)
大小:=uint64(len(partBytes))
如果大小{
结束=大小
}
chunk:=partBytes[开始:结束]
blockId:=base64.StdEncoding.EncodeToString(块)
块:=块{blockId,storage.BlockStatusCommitted}
块[i]=块
err=blobClient.PutBlock(容器、文件名、块ID、块)
如果出错!=零{
.......
}
}
err=blobClient.PutBlockList(容器、文件名、块)
如果出错!=零{
.......
}
}

希望有帮助。

你好,马克,谢谢你的回答。我试图看看是否可以将部分内容直接传递给CreateBlockBlobFromReader,但我从Azure得到一个错误,说正文长度为0,因此与内容长度不匹配(我作为“读取”传递)。你知道为什么吗?我将再次尝试直接传递部分内容。Andrew看到了上面Peter的有用答案。感谢您回答@Peter,您知道为什么CreateBlockBlobFromReader与reader一起需要前面的大小吗?@Mark Azure Storage SDK for Golang或其他语言是包装存储REST API。rest请求中的/require
Content-Length
头。