I read multiple blogs about saving the file using mgo, but unable to find a solution for below specific need, help shoutout!
Below inserts object in MongoDb:
var dbSchoolPojo dbSchool
i := bson.NewObjectId()
dbSchoolPojo.ID = i
coll := db.C("school")
coll.Insert(&dbSchoolPojo)
Below able to get hold of file:
file, handler, err := r.FormFile("pdf") //Able to get file from r *http.Request
Now, before inserting object, I need to set above file like:
dbSchoolPojo.pdf = file.Bytes(); //Of course Bytes() is invalid method
My structs object:
type dbSchool struct {
ID bson.ObjectId `json:"id" bson:"_id,omitempty"`
...
Pdf []byte `json:"pdf" bson:"pdf"`
...
}
In layman's term, the question is: How do I insert file (received from HTML form) in mongoDb via GoLang struct using mgo driver?
Thanks for reading! :)
Update:
PDF is stored in MongoDB like below:
Binary('EWHKDSH876KJHlkjdswsddsfsd3232432njnjkn2dljDSFSDFIJSFD333...')
Below code works without error, but doesn't serve PDF file:
func DownloadPdf(w http.ResponseWriter, r *http.Request, db mongoDB) {
var school dbSchool
coll := db.C("schools")
incomingId = "59e6404e2f68182a74610f19"; //This mongo DB _id is received from GET URL request
err := coll.Find(bson.M{"_id": bson.ObjectIdHex(incomingId)}).
Select(bson.M{"pdf": 1}).One(&school)
if err != nil {
serve404(w, r, db)
return
}
buffer := school.Pdf
w.Header().Set("Content-Disposition", "attachment; filename=abc.pdf")
w.Header().Set("Content-Type", "application/pdf")
w.Header().Set("Content-Length", strconv.Itoa(len(buffer)))
if _, err := w.Write(buffer); err != nil {
log.Println("unable to serve image.") //This line is not executed
}
}
JQuery code to request content:
$(".downloadPdfFile").click(function() {
var domain = document.location.origin;
window.open(domain+'/brochure/59e6404e2f68182a74610f19', '_blank');
});
The file
returned by Request.FormFile()
is of type multipart.File
which is:
type File interface {
io.Reader
io.ReaderAt
io.Seeker
io.Closer
}
It implements io.Reader
, so you can simply read its content e.g. with ioutil.ReadAll()
:
data, err := io.ReadAll(file)
// Handle error
Then:
dbSchoolPojo.Pdfdata = data
But storing big files as part of documents is not optimal / efficient. Instead take a look at the MongoDB GridFS that is also supported by mgo
: Database.GridFS()
.
Here's how you can store a file in MongoDB GridFS (example taken from GridFS.Create()
):
func check(err error) {
if err != nil {
panic(err.String())
}
}
file, err := db.GridFS("fs").Create("myfile.txt")
check(err)
n, err := file.Write([]byte("Hello world!"))
check(err)
err = file.Close()
check(err)
fmt.Printf("%d bytes written
", n)
Using GridFS
, you can also save the file without having to read all its content into memory first, if you "stream" the file content into the mgo.GridFile
, as it implements io.Writer
. Call io.Copy()
:
// ...
file, handler, err := r.FormFile("pdfFile")
// ...
mongoFile, err := db.GridFS("fs").Create("somepdf.pdf")
check(err)
// Now stream from HTTP request into the mongo file:
written, err := io.Copy(mongoFile, file)
// Handle err
err = mongoFile.Close()
check(err)
Edit: update answering your update
When you serve the PDF, you query the document in a wrong way:
err := coll.Find(bson.M{"_id": bson.ObjectIdHex(incomingId)}).
Select(bson.M{"pdf": 1}).One(&school)
You select the pdf
field to retrieve, but your documents in MongoDB have no such field:
type dbSchool struct {
ID bson.ObjectId `json:"id" bson:"_id,omitempty"`
...
Pdfdata []byte `json:"pdf"`
...
}
This type definition will result having a pdfdata
field, but not pdf
. Add the proper mgo
struct tag:
type dbSchool struct {
ID bson.ObjectId `json:"id" bson:"_id,omitempty"`
...
Pdfdata []byte `json:"pdf" bson:"pdf"`
...
}
Or change the selected field from pdf
to pdfdata
.