Golang通过multipart处理图像并流式传输到Azure

时间:2017-04-03 14:30:44

标签: azure go image-uploading azure-storage-blobs

在学习golang的过程中,我尝试编写具有多种图片上传功能的网络应用。

我使用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?

非常感谢任何帮助!

2 个答案:

答案 0 :(得分:3)

正如@Mark所说,您可以使用ioutil.ReadAll将内容读入字节数组,代码如下所示。

import (
   "bytes"
   "io/ioutil"
)

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

根据CreateBlockBlobFromReader的godoc,如下所示。

  

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

因此,如果大小超过64MB,则代码应如下所示。

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 {
      .......
   }
}

希望它有所帮助。

答案 1 :(得分:0)

Reader可以返回io.EOF和有效的最终字节读取,看起来最终字节(cBytes)没有添加到read个总字节。另外,注意:如果io.EOF以外的part.Read(buffer)返回错误,则读取循环可能不会退出。请改为考虑ioutil.ReadAll

CreateBlockBlobFromReader采用Reader,部分采用Reader,因此您可以直接传递部件。

您可能还想考虑Azure块大小限制可能小于图像,请参阅Asure blobs