2017-04-03 24 views
1

在學習golang的過程中,我試圖編寫一個具有多個圖像上傳功能的Web應用程序。Golang處理圖像通過多部分和流到Azure

我正在使用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

A Reader可以返回一個io.EOF和一個有效的最終字節讀取,它看起來像最終字節(cBytes)不會被添加到read總字節。另外,請注意:如果除了io.EOF之外的錯誤是由part.Read(buffer)返回的,則讀取循環可能不會退出。改爲考慮ioutil.ReadAll

CreateBlockBlobFromReader需要一個閱讀器,而零件是一個閱讀器,因此您可能能夠直接傳遞該零件。

您可能還想考慮Azure塊大小限制可能比圖片小,請參閱Asure blobs

+0

嗨馬克感謝回答。我試圖查看是否可以直接將部分內容傳遞給CreateBlockBlobFromReader,但是我從Azure中得到一個錯誤,說該體長度爲0,因此與Content-Length(我以「read」傳入的內容)不匹配。 。你知道這是爲什麼嗎?我會嘗試再次直接傳遞。 –

+0

安德魯見上面彼得的有用答案。 – Mark

1

正如@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) 

根據godoc爲CreateBlockBlobFromReader,如下所示。

的API 拒絕請求與大小> 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 { 
     ....... 
    } 
} 

希望它有幫助。

+0

感謝您回答@Peter,你知道爲什麼CreateBlockBlobFromReader需要讀者的大小和前面一樣嗎? – Mark

+1

@用於Golang或其他語言的Mark Azure Storage SDK包裝了Storage REST API。 ['Put Put Blob'](https://docs.microsoft.com/en-us/rest/api/storageservices/fileservices/put-blob)/ ['Put Block'](https://docs.microsoft.com/zh-/com/en-us/rest/api/storageservices/fileservices/put-block)需要在剩餘請求中包含「Content-Length」標頭。 –