摘要:[MongoDB]如何用GridFS上傳以及下載檔案(upload,download)
先介紹最新版本的:C# Driver 2.1:
現在官方強制提供非同步版本的function,與的Stephen的教學中心思想相異:Task.Run Etiquette Examples: Don't Use Task.Run in the Implementation
上傳下載的function應該寫成同步版本的,然後是否非同步執行,再交由caller去決定是否加上await Task.Run以非同步執行
但~既然官方只提供非同步的function,就只好配合他們的作法
上傳(非同步版本):
.aspx:
<asp:Button ID="btnUpload" runat="server" OnClick="btnUpload_Click" Text="非同步上傳" />
code_behind:
protected static IMongoClient _client;
protected static IMongoDatabase _database;
protected static string strFileName = @"test.pdf";
protected static string strNewFileName = @"testInGridFS.pdf";
protected static string strFilePath = @"D:\\temp\\";
protected void Page_Load(object sender, EventArgs e)
{
//此兩行初始化MongoClient, 利用client存取資料庫,建議放在Constructor
_client = new MongoClient();
_database = _client.GetDatabase("test");
}
//非同步上傳
protected async void btnUpload_Click(object sender, EventArgs e)
{
GridFSBucketOptions bucketOptions = new GridFSBucketOptions();
bucketOptions.BucketName = "myBucket";
var fs = new GridFSBucket(_database, bucketOptions);
string id = "";
using (var s = File.OpenRead(strFilePath + strFileName))
{
ObjectId oid = await fs.UploadFromStreamAsync(strNewFileName, s);
//下面這樣的寫法也可以得到一樣的結果,不過
//正統的方式應該是caller端自行決定是否使用await Task.Run(),而且 implement function內部沒有使用Task.Run
//但是官方提供的function已經強制在UploadFromStreamAsync裡面加上Task.Run
//因此原本這裡的caller端的await Task.Run反而是造成合計有兩次的Task.Run了
//雖然這樣執行結果也是正確拉....所以為了避免兩次的Task.Run
//這邊就只用await而不用正統的await Task.Run
//ObjectId oid = await Task.Run(() => fs.UploadFromStreamAsync(strNewFileName, s));
id = oid.ToString();
}
Page.ClientScript.RegisterStartupScript(this.GetType(), "", "alert('upload success!, id=' + '" + id + "');", true);
}
如果硬要把官方的function當成同步的方式去執行,原本應該是要極力避免的
但是總是有不可避免的時候,可利用Task.Run()去呼叫,原因可以看此文:
Should I expose synchronous wrappers for asynchronous methods?
http://blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx
上傳(同步版本):
.aspx:
<asp:Button ID="btnUploadSync" runat="server" OnClick="btnUploadSync_Click" Text="同步上傳" />
code behind:
protected void btnUploadSync_Click(object sender, EventArgs e)
{
GridFSBucketOptions bucketOptions = new GridFSBucketOptions();
bucketOptions.BucketName = "myBucket";
var fs = new GridFSBucket(_database, bucketOptions);
string id = "";
using (var s = File.OpenRead(strFilePath + strFileName))
{
Task t = Task.Run(() => fs.UploadFromStreamAsync(strNewFileName, s));
id = t.Result.ToString();
}
Page.ClientScript.RegisterStartupScript(this.GetType(), "", "alert('upload success!, id=' + '" + id + "');", true);
}
下載(非同步版本):
.aspx:
<asp:Button ID="btnAsyncDownload" runat="server" OnClick="btnAsyncDownload_Click" Text="AsyncDownload" />
code behind:
//非同步下載 下載的檔案直接顯示在目前網頁
//顯示在目前網頁只是最後呈現的方式之一,可以自行決定為其他形式(例如:直接下載在本機檔案)
protected async void btnAsyncDownload_Click(object sender, EventArgs e)
{
GridFSBucketOptions bucketOptions = new GridFSBucketOptions();
bucketOptions.BucketName = "myBucket";
var fs = new GridFSBucket(_database, bucketOptions);
//byte[] byteFile = await fs.DownloadAsBytesByNameAsync(strNewFileName);
ObjectId objID = new ObjectId("5628a96e23ed142638c6225c");
GridFSDownloadOptions options = new GridFSDownloadOptions();
System.Threading.CancellationToken token = new System.Threading.CancellationToken();
//以下參數只帶入objID的寫法正確,但是執行階段錯誤,因為官網提供的function有bug
//根據以下網頁,只要把非必填的參數也傳入,不要預設為null即可
//https://jira.mongodb.org/browse/CSHARP-1418
//byte[] byteFile = await fs.DownloadAsBytesAsync(objID);//這個寫法在執行階段會錯誤
byte[] byteFile = await fs.DownloadAsBytesAsync(objID, options, token);
//直接顯示在目前網頁
System.Web.HttpContext.Current.Response.ClearHeaders();
System.Web.HttpContext.Current.Response.Clear();
System.Web.HttpContext.Current.Response.Buffer = false;
System.Web.HttpContext.Current.Response.AddHeader("Content-Type", "application/pdf");
System.Web.HttpContext.Current.Response.AddHeader("Content-Length", byteFile.Length.ToString());
System.Web.HttpContext.Current.Response.BinaryWrite(byteFile);
System.Web.HttpContext.Current.Response.Flush();
System.Web.HttpContext.Current.Response.End();
}
下載(同步版本):
//同步下載 下載的檔案直接顯示在目前網頁
//顯示在目前網頁只是最後呈現的方式之一,可以自行決定為其他形式(例如:直接下載在本機檔案)
protected void btnSyncDownload_Click(object sender, EventArgs e)
{
GridFSBucketOptions bucketOptions = new GridFSBucketOptions();
bucketOptions.BucketName = "myBucket";
var fs = new GridFSBucket(_database, bucketOptions);
ObjectId objID = new ObjectId("5628a96e23ed142638c6225c");
GridFSDownloadOptions options = new GridFSDownloadOptions();
System.Threading.CancellationToken token = new System.Threading.CancellationToken();
Task t = Task.Run(() => fs.DownloadAsBytesAsync(objID, options, token));
byte[] bytes = (byte[])t.Result;
System.Web.HttpContext.Current.Response.ClearHeaders();
System.Web.HttpContext.Current.Response.Clear();
System.Web.HttpContext.Current.Response.Buffer = false;
System.Web.HttpContext.Current.Response.AddHeader("Content-Type", "application/pdf");
System.Web.HttpContext.Current.Response.AddHeader("Content-Length", bytes.Length.ToString());
System.Web.HttpContext.Current.Response.BinaryWrite(bytes);
System.Web.HttpContext.Current.Response.Flush();
System.Web.HttpContext.Current.Response.End();
}
然後介紹舊版的c# driver 1.8.234:
ps.每個版本的driver差異很大,到1.9的版本的時候,就不一定是這麼寫了,要注意
.aspx:
<div>
<asp:Button ID="UploadFileBtn" runat="server" Text="Upload" OnClick="UploadFileBtn_Click" />
<asp:Label ID="ObjectIDLabel" runat="server" ></asp:Label>
</div>
.aspx.cs
private static MongoClient client;
private static MongoServer server;
private static MongoDatabase database;
protected void Page_Load(object sender, EventArgs e)
{
client = new MongoClient("mongodb://localhost");
server = client.GetServer();
database = server.GetDatabase("tesdb");
}
protected void UploadFileBtn_Click(object sender, EventArgs e)
{
//var client = new MongoClient("mongodb://localhost");
//var server = client.GetServer();
//var server = MongoServer.Create("mongodb://localhost:27020");
//var database = server.GetDatabase("tesdb");
var fileName = "D:\\temp\\test.pdf";
//用stream上傳:OK
//using (var fs = new FileStream(fileName, FileMode.Open))
//{
// var gridFsInfo = database.GridFS.Upload(fs, fileName);
//}
//用檔案上傳:OK
var gridFsInfo = database.GridFS.Upload(fileName);
ObjectIDLabel.Text = gridFsInfo.Id.ToString();
Page.ClientScript.RegisterStartupScript(this.GetType(), "", "alert('上傳成功!')", true);
}
下載:
.aspx:
<div>
請輸入ObjectID:<asp:TextBox ID="ObjectIDText" runat="server"></asp:TextBox>
<asp:Button ID="DownloadFileBtn" runat="server" Text="Download" OnClick="DownloadFileBtn_Click" />
</div>
.aspx.cs:
private static MongoClient client;
private static MongoServer server;
private static MongoDatabase database;
protected void Page_Load(object sender, EventArgs e)
{
client = new MongoClient("mongodb://localhost");
server = client.GetServer();
database = server.GetDatabase("tesdb");
}
protected void DownloadFileBtn_Click(object sender, EventArgs e)
{
ObjectId oid = new ObjectId(ObjectIDText.Text.Trim());
string newFileName = "D:\\temp\\testInGridFS.pdf";
//讀出來是個GridFSStream物件
var file = database.GridFS.FindOne(Query.EQ("_id", oid));
//將GridFSStream轉成stream型態
using (var stream = file.OpenRead())
{
//把stream轉成byte[]
var bytes = new byte[stream.Length];
stream.Read(bytes, 0, (int)stream.Length);
//將byte[]寫入到檔案
using (var newFs = new FileStream(newFileName, FileMode.Create))
{
newFs.Write(bytes, 0, bytes.Length);
}
}
Page.ClientScript.RegisterStartupScript(this.GetType(), "", "alert('下載成功!')", true);
}
以上內容參考StackOverFlow:
MongoDB GridFs with C#, how to store files such as images?
http://stackoverflow.com/questions/4988436/mongodb-gridfs-with-c-how-to-store-files-such-as-images