I want to download document from Google Drive.for that I am using Google Drive API. I am very new to Google Drive API so can any one tell me how to download document from google Drive ?
I have try this option from here but I am getting exception on line
HttpWebRequest request = (HttpWebRequest)WebRequest.Create( new Uri(file.DownloadUrl.ToString()));
authenticator.ApplyAuthenticationToRequest(request); HttpWebResponse response = (HttpWebResponse)request.GetResponse();
like
The underlying connection was closed: An unexpected error occurred on
a send.
Unable to read data from the transport connection: An existing
connection was forcibly closed by the remote host.
I thing this problem occur because of scope. please tell me how to set scope in application
pls not I am creating desktop application in c# .net
can any one help me ?
This code works for me:
var stream = service.HttpClient.GetStreamAsync(file.DownloadUrl);
var result = stream.Result;
using (var fileStream = System.IO.File.Create(filePathToSaveFileOnDisk))
{
result.CopyTo(fileStream);
}
Have a look at the code example here: https://developers.google.com/drive/v2/reference/files/get
I am using this code to download files from google drive
public void Download(object sender, DoWorkEventArgs e)
{
List<File> driveFiles = Google.Apis.Util.Utilities.RetrieveAllFiles(service);
int fileCount = driveFiles.Count;
int i = 0;
IAuthenticator authenticator = new CloudManager().CreateAuthentication();
foreach (var driveFile in driveFiles.Where(driveFile => driveFile.MimeType != "video/mp4"))
{
LabelFileProcess.Dispatcher.BeginInvoke(DispatcherPriority.Background,
(Action) (() => LabelFileProcess.Content = driveFile.Title));
string title = driveFile.Title;
LabelFileProcess.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
(Action) (() => LabelFileProcess.Content = title));
if (string.IsNullOrEmpty(driveFile.Title))
{
MessageBox.Show(#"File's title is emplty");
continue;
}
if (driveFile.MimeType != "application/vnd.google-apps.folder")
{
Stream stream = Utilities.DownloadFile(authenticator, driveFile);
if (stream != null)
Utilities.SaveFile(stream, driveFile.Title);
}
else
Directory.CreateDirectory("D:\\GdriveFiles\\" + driveFile.Title);
}
Main Download processing methods
public void Download(object sender, DoWorkEventArgs e)
{
List<File> driveFiles = Google.Apis.Util.Utilities.RetrieveAllFiles(service);
int fileCount = driveFiles.Count;
int i = 0;
IAuthenticator authenticator = new CloudManager().CreateAuthentication();
foreach (var driveFile in driveFiles.Where(driveFile => driveFile.MimeType != "video/mp4"))
{
LabelFileProcess.Dispatcher.BeginInvoke(DispatcherPriority.Background,
(Action) (() => LabelFileProcess.Content = driveFile.Title));
string title = driveFile.Title;
LabelFileProcess.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
(Action) (() => LabelFileProcess.Content = title));
if (string.IsNullOrEmpty(driveFile.Title))
{
MessageBox.Show(#"File's title is emplty");
continue;
}
if (driveFile.MimeType != "application/vnd.google-apps.folder")
{
Stream stream = DownloadFile(authenticator, driveFile);
if (stream != null)
SaveFile(stream, driveFile.Title);
}
else
Directory.CreateDirectory("D:\\GdriveFiles\\" + driveFile.Title);
}
public static System.IO.Stream DownloadFile(IAuthenticator authenticator, File file)
{
if (!string.IsNullOrEmpty(file.DownloadUrl))
try
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(new Uri(file.DownloadUrl));
authenticator.ApplyAuthenticationToRequest(request);
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
return response.StatusCode == HttpStatusCode.OK ? response.GetResponseStream() : null;
}
catch (Exception e)
{
System.Windows.Forms.MessageBox.Show("Exception occures " + e.Message);
}
else
System.Windows.Forms.MessageBox.Show(#"File doesn't have any content on Drive, Title: "+file.Title);
return null;
}
public static void SaveFile(System.IO.Stream stream, String title)
{
StreamReader streamReader = new StreamReader(stream);
//System.Windows.MessageBox.Show(streamReader.ToString());
if (stream == null)
System.Windows.MessageBox.Show("Error Occured during download");
else
{
FileStream fileStream = System.IO.File.Create("D:\\GdriveFiles\\" + title);
char[] charArray = new char[100];
int count;// = streamReader.Read(arrayByte, 0, 100);
//streamReader.Read(arrayByte, 0, (int)stream.Length);
//fileStream.Write(arrayByte,0,arrayByte.Length);
string incomingMessage = "";
do
{
try
{
count = streamReader.Read(charArray, 0, 100);
incomingMessage += new string(charArray, 0, count);
byte[] byteArray = new byte[charArray.Length];
//byteArray = System.Text.Encoding.Unicode.GetBytes(charArray);
byteArray = System.Text.Encoding.ASCII.GetBytes(charArray);
fileStream.Write(byteArray, 0, count);
}
catch (ArgumentException ex)
{
MessageBox.Show(#"File ended, Exception:" + ex.Message);
break;
}
catch (Exception e)
{
MessageBox.Show("Exception occure" + e.Message);
break;
}
} while (count > 0);
fileStream.Close();
//MessageBox.Show("File Contains are " + incomingMessage);
}
}
Related
I am getting 400 error code with bad request while request to file upload API.
I built the back-end and front-end for file uploading in asp.net core and it works in localhost when I run it with IIS in my PC (using visual studio 2017).
Both of saving and updating API are working in my local but update API is not working if I deploy the code
front-end code like below:
public static async Task<HttpResponseMessage> UploadFile(string uploadUrl, string filePath, FFFileInfo fileInfo)
{
string fileName = fileInfo.Name + "." + fileInfo.Extension;
string contentType = MimeTypes.GetMimeType(filePath);
using (var hc = new HttpClient())
{
hc.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(TokenType, AccessToken);
hc.DefaultRequestHeaders.Accept.Clear();
hc.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
Stream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
StreamContent streamContent = CreateFileContent(fileStream, fileName, contentType);
// StreamContent streamContent = CreateFileContent(fileStream, "image.jpg", "image/jpeg"); // Multiple file upload
var requestContent = new MultipartFormDataContent("Upload Id" + DateTime.Now.ToString(CultureInfo.InvariantCulture));
requestContent.Add(streamContent, fileInfo.Name, fileName);
var progressContent = new ProgressableStreamContent(
requestContent,
4096,
(sent, total) =>
{
//Console.WriteLine("Uploading {0}/{1}", sent, total);
int percentage = (int) Math.Round((double)(100 * sent) / total);
Console.Write("\r{0}\t{1}%", fileInfo.Path, percentage);
if (sent == total)
{
Console.WriteLine();
}
});
var response = await hc.PostAsync(new Uri(uploadUrl), progressContent);
return response;
}
}
backend code like below:
[HttpPost]
[DisableFormValueModelBinding]
public async Task<IActionResult> UploadFiles([FromQuery] FFFileInfo fileinfo)
{
if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType))
{
return BadRequest($"Expected a multipart request, but got {Request.ContentType}");
}
authUser = User.ToAuthUser();
userId = authUser.UserId();
customerId = authUser.CustomerId();
Server.Model.File new_file = new Server.Model.File();
var boundary = MultipartRequestHelper.GetBoundary(MediaTypeHeaderValue.Parse(Request.ContentType), _defaultFormOptions.MultipartBoundaryLengthLimit);
var reader = new MultipartReader(boundary, HttpContext.Request.Body);
var section = await reader.ReadNextSectionAsync();
MemoryStream writeStream = new MemoryStream();
byte[] content = null;
while (section != null)
{
ContentDispositionHeaderValue contentDisposition;
var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out contentDisposition);
int chunkSize = 1024;
byte[] byte_file = new byte[chunkSize];
int bytesRead = 0;
new_file.File_Content = byte_file;
if (hasContentDispositionHeader)
{
if (MultipartRequestHelper.HasFileContentDisposition(contentDisposition))
{
//await section.Body.CopyToAsync(targetStream);
using (var byte_reader = new BinaryReader(section.Body))
{
do
{
bytesRead = byte_reader.Read(byte_file, 0, byte_file.Length);
if(bytesRead <= 0)
{
content = writeStream.ToArray();
}
writeStream.Write(byte_file, 0, bytesRead);
} while (bytesRead > 0);
content = writeStream.ToArray();
}
}
}
// Drains any remaining section body that has not been consumed and
// reads the headers for the next section.
section = await reader.ReadNextSectionAsync();
}
try
{
new_file = new Server.Model.File
{
File_Name = fileinfo.Name,
File_Path = fileinfo.Path,
File_Ext = fileinfo.Extension,
Check_Sum = fileinfo.Checksum,
ToolSerialNumber = fileinfo.ToolSerialNumber,
FileSize = fileinfo.Length,
File_Content = content,
UserId = userId,
CustomerId = customerId
};
}
catch (Exception ex)
{
return BadRequest(ex);
}
try
{
if (!fileService.isExist(new_file.File_Path, userId))
{
fileService.SaveFile(new_file);
}
else
{
Server.Model.File existing = fileService.GetFileByPath(new_file.File_Path, userId);
fileService.UpdateFile(existing, new_file);
}
//set file content to null to response with small data
new_file.File_Content = null;
return Ok(new_file);
}
catch (Exception ex)
{
logger.LogError("DB action error {0}", ex.ToString());
return BadRequest(ex);
}
}
As you can see the above code, saving and updating are using same code but only updating is not working when it is deployed.
It is very strange for me.
I found the solution.
This code was deployed by my client I couldn't check the database that he deployed.
Based on researching and testing, I got an idea that might be related with permission issue.
So, we check it for db.
At the end, we found that current user has insert, delete, select permission but have not update permission.
After granting the update permission, it is working perfectly
I have a problem with streaming video. I developed the server on ASP.NET Web API 2 and implemented 2 methods:
The first method:
if (Request.Headers.Range != null)
{
try
{
var httpResponce = Request.CreateResponse();
httpResponce.Content =
new PushStreamContent((Action<Stream, HttpContent, TransportContext>) WriteContentToStream);
return httpResponce;
}
catch (Exception ex)
{
return new HttpResponseMessage(HttpStatusCode.InternalServerError);
}
}
else
{
return new HttpResponseMessage(HttpStatusCode.RequestedRangeNotSatisfiable);
}
/*method for streaming*/
private async void WriteContentToStream(Stream outputStream, HttpContent content, TransportContext transportContext)
{
string relativeFilePath = "~/App_Data/Videos/4.mp4";
try
{
var filePath = System.Web.Hosting.HostingEnvironment.MapPath(relativeFilePath);
int bufferSize = 1000;
byte[] buffer = new byte[bufferSize];
using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
int totalSize = (int)fileStream.Length;
while (totalSize > 0)
{
int count = totalSize > bufferSize ? bufferSize : totalSize;
int sizeOfReadedBuffer = fileStream.Read(buffer, 0, count);
await outputStream.WriteAsync(buffer, 0, sizeOfReadedBuffer);
totalSize -= sizeOfReadedBuffer;
}
}
}
catch (HttpException ex)
{
if (ex.ErrorCode == -2147023667)
{
return;
}
}
finally
{
outputStream.Close();
}
}
2) The second method:
public HttpResponseMessage Test()
{
if (Request.Headers.Range != null)
{
try
{
string relativeFilePath = "~/App_Data/Videos/4.mp4";
var filePath = System.Web.Hosting.HostingEnvironment.MapPath(relativeFilePath);
HttpResponseMessage partialResponse = Request.CreateResponse(HttpStatusCode.PartialContent);
partialResponse.Headers.AcceptRanges.Add("bytes");
var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
partialResponse.Content = new ByteRangeStreamContent(stream, Request.Headers.Range, new MediaTypeHeaderValue("video/mp4"));
return partialResponse;
}
catch (Exception)
{
return new HttpResponseMessage(HttpStatusCode.InternalServerError);
}
}
else
{
return new HttpResponseMessage(HttpStatusCode.RequestedRangeNotSatisfiable);
}
}
Both of these methods worked on Web-client and Android-client, but iOS-client doesn't show video.
I think, that problem may be with codec of video (but I used codecs, which recommend Apple) or http-headers.
I just solved this one, and it was because the Content-Length header had (what iOS considered to be) an invalid value.
My solution was based on method #2 above...
Here's the important part of my code that actually worked.
if (!file.Exists) {
response.StatusCode = HttpStatusCode.NotFound;
response.ReasonPhrase = "Deleted";
} else {
var range = Request.Headers.Range?.Ranges?.FirstOrDefault();
if (range == null) {
using (var stream = new MemoryStream()) {
using (var video = file.OpenRead()) await video.CopyToAsync(stream);
response.Content = new ByteArrayContent(stream.ToArray());
}
response.Content.Headers.ContentType = new MediaTypeHeaderValue("video/mp4");
response.Content.Headers.ContentLength = file.Length;
} else {
var stream = new MemoryStream();
using (var video = file.OpenRead()) await video.CopyToAsync(stream);
response.Content = new ByteRangeStreamContent(
stream,
new RangeHeaderValue(range.From, range.To),
new MediaTypeHeaderValue("video/mp4")
);
// response.Content.Headers.ContentLength = file.Length;
// this is what makes iOS work
response.Content.Headers.ContentLength = (range.To.HasValue ? range.To.Value + 1 : file.Length) - (range.From ?? 0);
}
response.StatusCode = HttpStatusCode.OK;
}
I should probably put in an HTTP 206 (partial content) status when dealing with ranges, but I was working on this for nearly two days before coming up with a solution.
The only problem I have yet to fully track down is that from time-to-time, the Application_EndRequest doesn't fire for some of these. I am able to log the response being sent by the endpoint, but it's like iOS disconnects the connection somewhere and the request hangs until it times out internally.
Check HLS streaming which required for iOS. You can not play video file directly pointing to video file.
https://developer.apple.com/streaming/
I am trying to ftpupload a zipfile with async/await pattern:
private async void button2_Click(object sender, RoutedEventArgs e)
{
await processFtp();
}
async Task processFtp()
{
string result = "";
string ftpHost = "ftp://mysite/mysubdir";
string ftpUser = "itsme";
string ftpPassword = "mypw";
string ftpfullpath = ftpHost + "/" + "OutdoorTest.zip";
string fileToUpload = #"c:\temp\Outdoorbilder.zip";
try
{
FtpWebRequest request = (FtpWebRequest)WebRequest.Create(ftpfullpath);
request.Method = WebRequestMethods.Ftp.UploadFile;
request.Credentials = new NetworkCredential(ftpUser,ftpPassword);
request.UseBinary = true;
request.KeepAlive = false;
request.ReadWriteTimeout = 1000000;
request.Timeout = 1000000;
using (Stream requestStream = request.GetRequestStream())
{
using (FileStream fs = File.OpenRead(fileToUpload))
{
byte[] b = new byte[10 * 1024];
int readLength = 0;
int sentLength = 0;
while ((readLength = fs.Read(b, 0, b.Length)) > 0)
{
await requestStream.WriteAsync(b, 0, b.Length);
int percentComplete = (int)((float)(sentLength += readLength) / (float)fs.Length * 100);
ftpprogress.Value = percentComplete;
}
requestStream.Close();
requestStream.Flush();
}
}
FtpWebResponse response = (FtpWebResponse)request.GetResponse();
response.Close();
result = response.StatusDescription;
}
catch (WebException e)
{
result = e.Message;
if (e.Status == WebExceptionStatus.ProtocolError)
{
result = result + "Status Code : " +
((FtpWebResponse)e.Response).StatusCode;
result = result + "\nStatus Description : " +
((FtpWebResponse)e.Response).StatusDescription;
}
}
catch (Exception e)
{
result = e.Message;
}
MessageBox.Show(result);
}
}
The code seems to work fine and I get a 226 response. But the zip file on the ftp server is arround 1000bytes biger than the original and after download to a mobile android device cannot be opend/extracted.
When I upload without async/await pattern the uploaded file has the same size on ftp server as the local original.
How/where does this happen?
This has nothing to do with async/await.
Your problem is that you are not telling the correct size to upload. Look at these two lines:
while ((readLength = fs.Read(b, 0, b.Length)) > 0)
{
await requestStream.WriteAsync(b, 0, b.Length);
You need to specify that the WriteAsyc writes the read amount and not the amount allocated for the byte buffer. At least the last read will return less than the buffer size.
So the correct code is:
while ((bytesRead = fs.Read(b, 0, b.Length)) > 0)
{
await requestStream.WriteAsync(b, 0, bytesRead);
I am trying to log in to a website from my C# web application to send emails directly from the application instead of making user visit the website personally.
NOTE: The website, I am trying to log into changes the id of the textboxes everytime the page is loaded, EVEN DURING THE SAME SESSION. Therefore I decided to read the page source, extract the id of the textbox and then use that id while posting the message.
public partial class sender : System.Web.UI.Page
{
string userID, userPwd, recepsID, msgText, loginResponseString, sessionCode, queryCode;
private HttpWebRequest initRequest, loginRequest, msgRequest;
private HttpWebResponse initResponse, loginResponse;
private Object lockObj = new Object();
protected void Page_Load(object sender, EventArgs e)
{
userID = Request.QueryString["userNumber"];
userPwd = Request.QueryString["userPwd"];
recepsID = Request.QueryString["receps"];
msgText = Request.QueryString["msgBody"];
if (userID != null && userPwd != null && recepsID != null & msgText != null)
doLoginAndSendMessage(userID, userPwd, recepsID, msgText);
else
Response.Write("Some values are missing");
}
public void doLoginAndSendMessage(string uid, string pwd, string recepIds, string msg)
{
try
{
doLogin(uid, pwd, recepIds, msg);
}
catch (Exception ex)
{
Response.Write("Sending Failed, Please Try Again");
}
}
public void doLogin(string strUserId, string strPassword, string strIds, string strMessage)
{
try
{
initRequest = (HttpWebRequest)WebRequest.Create("http://www.somewebsite.com/login.aspx");
initRequest.CookieContainer = new CookieContainer();
initRequest.Timeout = 60000;
StreamReader initSr = new StreamReader(initRequest.GetResponse().GetResponseStream(), System.Text.Encoding.GetEncoding("utf-8"));
initSr.ReadToEnd();
initSr.Close();
loginRequest = (HttpWebRequest)WebRequest.Create("http://www.somewebsite.com/login.aspx");
loginRequest.CookieContainer = new CookieContainer();
loginRequest.Timeout = 60000;
StringBuilder loginString = new StringBuilder();
loginString.Append("LoginUserId=" + strUserId + "&LoginPassword=" + strPassword + "&RememberMe=1&Login=Login");
byte[] loginData = Encoding.ASCII.GetBytes(loginString.ToString());
//to get any cookies from the initial response
initResponse = (HttpWebResponse)initRequest.GetResponse();
//setting cookies
loginRequest.CookieContainer.Add(initResponse.Cookies);
//Adding Headers
loginRequest.Method = "POST";
loginRequest.ContentType = "application/x-www-form-urlencoded";
loginRequest.ContentLength = loginData.Length;
Stream loginStream = loginRequest.GetRequestStream();
loginStream.Write(loginData, 0, loginData.Length);
loginStream.Close();
//Reading the response
StreamReader loginSr = new StreamReader(loginRequest.GetResponse().GetResponseStream(), System.Text.Encoding.GetEncoding("utf-8"));
loginResponseString = loginSr.ReadToEnd();
loginSr.Close();
if (loginResponseString.Contains("inbox.aspx"))
{
//get session code
sessionCode = loginResponseString.Substring(125, 5);
//call the sendmessage method
sendMessage(strIds, strMessage);
}
else
{
Response.Write("Login Failed: Check Username and password");
}
}
catch (Exception ex)
{
Response.Write("Sending Failed, Please Try Again");
}
}
public void sendMessage(string strIds, string strMsg)
{
try
{
string[] ids = strIds.Split(',');
for (int i = 0; i < ids.Length; i++)
{
msgRequest = (HttpWebRequest)WebRequest.Create("http://www.somewebsite.com/writenew.aspx?sessionid=" + sessionCode);
msgRequest.CookieContainer = new CookieContainer();
msgRequest.Timeout = 1000000;
msgRequest.ReadWriteTimeout = 1000000;
msgRequest.SendChunked = true;
//to get any cookies from the initial response
loginResponse = (HttpWebResponse)loginRequest.GetResponse();
//setting cookies
msgRequest.CookieContainer.Add(loginResponse.Cookies);
//Adding Headers
msgRequest.Method = "POST";
msgRequest.ContentType = "application/x-www-form-urlencoded";
Stream msgStream = msgRequest.GetRequestStream();
Stream respStream = msgRequest.GetResponse().GetResponseStream();
StreamReader codeRead = new StreamReader(respStream, System.Text.Encoding.GetEncoding("utf-8"));
string temp = codeRead.ReadToEnd();
codeRead.Close();
respStream.Close();
txtResponse.Text = temp;
try
{
int starInd = temp.IndexOf("UserId_");
//int endInd = starInd + 15;
string holder = temp.Substring(starInd, 15);
int startInd = holder.IndexOf("_") + 1;
queryCode = holder.Substring(startInd, 5);
txtSubString.Text = queryCode;
}
catch (Exception ex)
{
txtSubString.Text = "SOME ERROR";
}
lock (lockObj)
{
StringBuilder msgString = new StringBuilder();
msgString.Append("sessionid=" + queryCode + "&GlobalKeyId=1&MessageLength=988&ReceiveId_"
+ queryCode + "=" + ids[i] + "&Message_" + queryCode + "=" + strMsg
+ "&SendNow_" + queryCode + "=Send Now");
byte[] msgData = Encoding.ASCII.GetBytes(msgString.ToString());
msgStream.Write(msgData, 0, msgData.Length);
msgStream.Close();
}
//Reading the response
StreamReader msgSr = new StreamReader(respStream, System.Text.Encoding.GetEncoding("utf-8"));
string msgResponseString = msgSr.ReadToEnd();
msgSr.Close();
sessionCode = msgResponseString.Substring(123, 5);
}
Response.Write("Message Sent Successfully");
}
catch (Exception ex)
{
Response.Write("Sending Failed, Please Try Again<br/>" + ex.Message);
}
}
}
The application stops when it reaches this line
msgStream.Write(msgData, 0, msgData.Length);
Please help me solve the error. Thank You
When you call GetResponse() it will cause the request built so far to be sent, and the client to fetch the response.
You need to build your complete request before calling GetResponse(), or your request won't be complete. Getting the request stream and writing POST data after GetResponse() was called will throw this exception to show that continuing building the request after it has already been sent makes no sense.
How to save the webpage using c#? I need to open a dialog asking for the path to save the file.
Any help?
Create a file chooser like explained on this blog .
And then a web client
WebClient Client = new WebClient ();
Client.DownloadFile("pagename", " saveasname");
Here's another way:
private string DownlodHTMLPage(Uri url)
{
WebResponse response = null;
Stream stream = null;
StreamReader sr = null;
try
{
HttpWebRequest hwr = (HttpWebRequest)WebRequest.Create(url);
//sometimes it doesn't work if user agent is not set
hwr.UserAgent = "Opera/9.80 (Windows NT 5.1; U; pl) Presto/2.2.15 Version/10.10";
response = hwr.GetResponse();
stream = response.GetResponseStream();
//check if content type of page is text/xxx. you can add statement for XHTML files
if (!response.ContentType.ToLower().StartsWith("text/"))
{
return null;
}
string buffer = "", line;
//get the stream reader
sr = new StreamReader(stream);
//download HTML to buffer
while ((line = sr.ReadLine()) != null)
{
buffer += line + "\r\n"; //line with new line markers
}
return buffer;
}
catch (WebException e)
{
System.Console.WriteLine("Can't download from " + url + " 'casue " + e);
return null;
}
catch (IOException e)
{
System.Console.WriteLine("Can't download from " + url + " 'cause " + e);
return null;
}
finally
{
if (sr != null)
sr.Close();
if (stream != null)
stream.Close();
if (response != null)
response.Close();
}
}
Edit
To answer the question in comment of Ranjana. Method above just download a web page and returns it as a string. You save it later using e.g StreamWriter:
StreamWriter writer = new StreamWriter(PATH_TO_FILE, false, Encoding.UTF8);
writer.Write(DownlodHTMLPage(uri));
Path to file you can get using SaveFileDialog, e.g.:
SaveFileDialog dialog = new SaveFileDialog();
if (dialog.ShowDialog() == DialogResult.OK)
{
string file = dialog.FileName;
//rest of the code comes here
}
I hope that this is what you were asking for.