[MongoDB]如何用GridFS上傳以及下載檔案(upload,download), how to upload and download files to and from GRIDFS

  • 1374
  • 0
  • 2016-04-13

摘要:[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