2014-03-03 159 views
3

noob Golang和Sinatra人在這裏。我已經破解了Sinatra應用程序,接受從HTML表單發佈的上傳文件,並通過GridFS將其保存到託管的MongoDB數據庫中。這似乎工作正常。我正在使用mgo驅動程序在Golang中編寫相同的應用程序。在MongoDB GridFS中存儲上傳的文件使用mgo不保存到內存

功能上它工作正常。但是,在我的Golang代碼中,我將文件讀入內存,然後使用mgo將文件從內存寫入MongoDB。這看起來比我等同的Sinatra應用程序慢得多。我感覺到Rack和Sinatra之間的交互不執行這個「中間」或「臨時」步驟。

這裏是我的Go代碼片段:

func uploadfilePageHandler(w http.ResponseWriter, req *http.Request) { 
    // Capture multipart form file information 
    file, handler, err := req.FormFile("filename") 
    if err != nil { 
    fmt.Println(err) 
    } 

    // Read the file into memory 
    data, err := ioutil.ReadAll(file) 
    // ... check err value for nil 

    // Specify the Mongodb database 
    my_db := mongo_session.DB("... database name...") 

    // Create the file in the Mongodb Gridfs instance 
    my_file, err := my_db.GridFS("fs").Create(unique_filename) 
    // ... check err value for nil 

    // Write the file to the Mongodb Gridfs instance 
    n, err := my_file.Write(data) 
    // ... check err value for nil 

    // Close the file 
    err = my_file.Close() 
    // ... check err value for nil 

    // Write a log type message 
    fmt.Printf("%d bytes written to the Mongodb instance\n", n) 

    // ... other statements redirecting to rest of user flow... 
} 

問題

  • 這是 「臨時」 的步驟需要(data, err := ioutil.ReadAll(file))?
  • 如果是這樣,我可以更有效地執行此步驟嗎?
  • 我應該考慮其他可接受的做法或方法嗎?

謝謝...

+0

這主要取決於你所使用MongoDB的驅動程序。我發現這個庫支持逐漸寫入文件,例如:http://godoc.org/github.com/sunfmin/tenpu/gridfs,但我真的不知道gridfs或你正在使用的庫,所以可以'幫助很大。 –

+0

你說過「這看起來比我等同的Sinatra應用程序慢得多。」。我無法幫助你,因爲我對MongoDB一無所知。如果你有一個例子來證明Sinatra和Go之間的速度差異,我很樂意看到它。 –

+0

Eric ...對我的評論掃描到Gustavo。認爲Go/mgo版本的代碼較慢是由於我的缺陷方法(先讀取內存中的文件內容)。在Sinatra/Ruby Driver版本中,我沒有明確採取這一步驟。因此,感覺到的緩慢對我來說是一種跡象,表明我正在做一些效率低下的事情(而不是執行速度的根本差異) – user2471982

回答

7

不,你不應該完全在內存中讀取文件一次,因爲當文件過大,將打破。在documentation for GridFS.Create第二個例子避免了這個問題:

file, err := db.GridFS("fs").Create("myfile.txt") 
check(err) 
messages, err := os.Open("/var/log/messages") 
check(err) 
defer messages.Close() 
err = io.Copy(file, messages) 
check(err) 
err = file.Close() 
check(err) 

至於爲什麼它比其他東西,很難不潛水告訴到使用這兩種方式的細節慢。

+1

Gustavo謝謝!這絕對是我需要去的方向。沒有執行速度的具體問題。認爲Go/mgo版本比Sinatra/Ruby Driver版本慢是因爲我的缺陷(即將文件讀入內存而不是打開文件並將內容複製到新的GridFS文件)。我假設我需要有效地「打開」通過多部分表單發佈的文件,然後將其複製到新的GridFS文件中。我將深入研究適當的Go包來了解更多。 – user2471982

1

從multipartForm獲取文件後,可以使用下面的函數將其保存到GridF中。我也測試了這個巨大的文件(高達570MB)。

//....code inside the handlerfunc 
for _, fileHeaders := range r.MultipartForm.File { 
     for _, fileHeader := range fileHeaders { 
      file, _ := fileHeader.Open()    
      if gridFile, err := db.GridFS("fs").Create(fileHeader.Filename); err != nil { 
       //errorResponse(w, err, http.StatusInternalServerError) 
       return 
      } else { 
       gridFile.SetMeta(fileMetadata) 
       gridFile.SetName(fileHeader.Filename) 
       if err := writeToGridFile(file, gridFile); err != nil { 
        //errorResponse(w, err, http.StatusInternalServerError) 
        return 
       } 
func writeToGridFile(file multipart.File, gridFile *mgo.GridFile) error { 
    reader := bufio.NewReader(file) 
    defer func() { file.Close() }() 
    // make a buffer to keep chunks that are read 
    buf := make([]byte, 1024) 
    for { 
     // read a chunk 
     n, err := reader.Read(buf) 
     if err != nil && err != io.EOF { 
      return errors.New("Could not read the input file") 
     } 
     if n == 0 { 
      break 
     } 
     // write a chunk 
     if _, err := gridFile.Write(buf[:n]); err != nil { 
      return errors.New("Could not write to GridFs for "+ gridFile.Name()) 
     } 
    } 
    gridFile.Close() 
    return nil 
} 
相關問題