在学习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?
非常感谢任何帮助!
答案 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。