I'm having issues with the download process, it downloads, but the file downloaded is the same byte size: 108102 bytes; regardless of whether the actual file is smaller or bigger than this.
I'm able to upload a PDF file successfully and set its permissions to make it viewable by anyone with the link https://drive.google.com/open?id=UNIQUE_ID_HERE. Below is the function I use to download the file asynchronously:
/// <summary>Downloads the media from the given URL.</summary>
private async Task DownloadFile(DriveService service, string url)
{
var downloader = new MediaDownloader(service);
downloader.ChunkSize = DownloadChunkSize;
// add a delegate for the progress changed event for writing to console on changes
downloader.ProgressChanged += Download_ProgressChanged;
var fileName = DownloadDirectoryName + #"\cover_new.pdf";
Console.WriteLine("Downloading file from link: {0}", url);
using (var fileStream = new System.IO.FileStream(fileName, System.IO.FileMode.Create, System.IO.FileAccess.Write))
{
var progress = await downloader.DownloadAsync(url, fileStream);
if (progress.Status == DownloadStatus.Completed)
{
Console.WriteLine(fileName + " was downloaded successfully: " + progress.BytesDownloaded);
}
else
{
Console.WriteLine("Download {0} was interrupted in the middle. Only {1} were downloaded. ", fileName, progress.BytesDownloaded);
}
}
}
Also, I'm able to open this file using the link successfully from a different browser without any credentials entered.
I was incorrectly trying to download the file using a URL like: https://drive.google.com/open?id=UNIQUE_ID_HERE.
Changes I made to my method:
The second parameter, url, is now a file object. I was passing the URL, but instead I should fetch the file using service.Files.Get() and using its Id.
Rename the second parameter to fileToDownload, for clarity.
Since I now have a file object downloadfile for the file I want to download, I can call its DownloadAsync method, sending it the filestream I already had.
The new code below:
private async Task DownloadFile(DriveService service, Google.Apis.Drive.v3.Data.File fileToDownload)
{
var downloader = new MediaDownloader(service);
downloader.ChunkSize = DownloadChunkSize;
// add a delegate for the progress changed event for writing to console on changes
downloader.ProgressChanged += Download_ProgressChanged;
var fileName = DownloadDirectoryName + #"\cover_new.pdf";
var downloadfile = service.Files.Get(fileToDownload.Id);
Console.WriteLine("Downloading file with id: {0}", fileToDownload);
using (var fileStream = new System.IO.FileStream(fileName, System.IO.FileMode.Create, System.IO.FileAccess.Write))
{
var progress = await downloadfile.DownloadAsync(fileStream);
if (progress.Status == DownloadStatus.Completed)
{
Console.WriteLine(fileName + " was downloaded successfully: " + progress.BytesDownloaded);
}
else
{
Console.WriteLine("Download {0} was interrupted in the middle. Only {1} were downloaded. ", fileName, progress.BytesDownloaded);
}
}
}
I hope this helps someone out there.
Related
I making MVC website. I would like to use dropbox for storing my files.
I find some code hove to upload files but I have no idea how to return back file url.
How to upload file to dropbox and return uploaded file url to save in db?
My code:
async Task Upload(DropboxClient dbx, string folder, string file, string content)
{
using (var mem = new MemoryStream(Encoding.UTF8.GetBytes(content)))
{
var updated = await dbx.Files.UploadAsync(
folder + "/" + file,
WriteMode.Overwrite.Instance,
body: mem);
Console.WriteLine("Saved {0}/{1} rev {2}", folder, file, updated.Rev);
}
}
How to upload file to dropbox and return uploaded file url to save in
db?
Well, within upload file you would see Dropbox.Api.Sharing.CreateSharedLinkWithSettingsArg method which has the response type of SharedLinkMetadata class just like as following:
As you can see SharedLinkMetadata class has the property name URL which is :
public string Url { get; protected set; }
Above property would provide you filesharing URL metadata. Thus, you can update your Upload method as following:
public static async Task<string> UploadFile(string folder, string fileName, string fileUri)
{
var dropBoxClient = new DropboxClient("Token","AppKey");
using (var ms = new FileStream(fileUri, FileMode.Open, FileAccess.Read))
{
FileMetadata updated = await dropBoxClient.Files.UploadAsync(
folder + "/" + fileName,
WriteMode.Overwrite.Instance,
body: ms);
var shareLinkInfo = new Dropbox.Api.Sharing.CreateSharedLinkWithSettingsArg(folder + "/" + fileName);
var reponseShare = await dropBoxClient.Sharing.CreateSharedLinkWithSettingsAsync(shareLinkInfo);
return reponseShare.Url;
}
}
Note: If you still need further information on this, please have a look on dropbox official document here.
Using the Dropbox .NET SDK, to create a shared link for a file, you can use the CreateSharedLinkWithSettingsAsync method.
SharedLinkMetadata sharedLinkMetadata;
try
{
sharedLinkMetadata = await this.client.Sharing.CreateSharedLinkWithSettingsAsync(path:path);
}
catch (ApiException<CreateSharedLinkWithSettingsError> err)
{
if (err.ErrorResponse.IsSharedLinkAlreadyExists)
{
var sharedLinksMetadata = await this.client.Sharing.ListSharedLinksAsync(path:path, directOnly:true);
sharedLinkMetadata = sharedLinksMetadata.Links.First();
}
else
{
throw err;
}
}
Console.WriteLine(sharedLinkMetadata.Url);
Or, to create a temporary direct link, you can use the GetTemporaryLinkAsync method.
I have an API Upload Controller, which has a parameter IFormFile. From Swagger, I am passing a .zip file which has a few .json files inside. I want to get these .json files from that .zip that I receive from Swagger and pass them to a service that will process them.
So I managed to create a logic like this. I save the .zip file in (~Temp>settings) directory, the next thing I want to do is unzip that file and send the .json files into a different directory named "temp-json-imports". So then I can get the .json files and work with them.
Here is the code that I have written so far, this doesn't work, it fails on the last line - (ZipFile.ExtractToDirectory(filePath, tmpJsonImports);), with an exception of type System.IO.IOException (like shown in the picture below).
Any ideas on how can I solve this problem would be very much welcome. :)
[HttpPost("import/{applicationId}")]
public async Task<IActionResult> ImportSettings([FromRoute] Guid applicationId, IFormFile file)
{
string tempPath = Path.Combine(_hostingEnvironment.ContentRootPath, Path.GetTempPath());
string tmpSettingsPath = Path.Combine(tempPath, "settings");
string tmpImportSettings = Path.Combine(tmpSettingsPath, "import");
string tmpJsonImports = Path.Combine(tmpImportSettings, "temp-json-imports");
Directory.CreateDirectory(tmpSettingsPath);
Directory.CreateDirectory(tmpImportSettings);
Directory.CreateDirectory(tmpJsonImports);
long size = file.Length;
if (size > 0)
{
var filePath = tmpImportSettings + "\\" + file.FileName;
using var stream = new FileStream(filePath, FileMode.Create);
await file.CopyToAsync(stream);
string zipPath = Path.GetFileName(filePath);
ZipFile.ExtractToDirectory(filePath, tmpJsonImports);
}
return Ok();
}
Try to use your code on my application, it will show this exception:
This exception relates the following code, you didn't close the file handle after copy the file to the path.
var filePath = tmpImportSettings + "\\" + file.FileName;
using var stream = new FileStream(filePath, FileMode.Create);
await file.CopyToAsync(stream);
To solve this exception, try to modify your code as below:
if (size > 0)
{
var filePath = tmpImportSettings + "\\" + fileviewmodel.File.FileName;
using (var stream = new FileStream(filePath, FileMode.Create))
{
await fileviewmodel.File.CopyToAsync(stream);
};
string zipPath = Path.GetFileName(filePath);
ZipFile.ExtractToDirectory(filePath, tmpJsonImports);
}
I tried 'using' but it says that the method is not Idisposable. I checked for running processes in Task Manager, nothing there. My goal is to upload a file from local directory to the Rich Text editor in my website. Please help me resolve this issue. Thanks in Advance
public void OnPostUploadDocument()
{
var projectRootPath = Path.Combine(_hostingEnvironment.ContentRootPath, "UploadedDocuments");
var filePath = Path.Combine(projectRootPath, UploadedDocument.FileName);
UploadedDocument.CopyTo(new FileStream(filePath, FileMode.Create));
// Retain the path of uploaded document between sessions.
UploadedDocumentPath = filePath;
ShowDocumentContentInTextEditor();
}
private void ShowDocumentContentInTextEditor()
{
WordProcessingLoadOptions loadOptions = new WordProcessingLoadOptions();
Editor editor = new Editor(UploadedDocumentPath, delegate { return loadOptions; }); //passing path and load options (via delegate) to the constructor
EditableDocument document = editor.Edit(new WordProcessingEditOptions()); //opening document for editing with format-specific edit options
DocumentContent = document.GetBodyContent(); //document.GetContent();
Console.WriteLine("HTMLContent: " + DocumentContent);
//string embeddedHtmlContent = document.GetEmbeddedHtml();```
//Console.WriteLine("EmbeddedHTMLContent: " + embeddedHtmlContent);
}
FileStream is disposable, so you can use using on it:
using (var stream = new FileStream(filePath, FileMode.Create)
{
UploadedDocument.CopyTo(stream);
}
Question:
How can I tell my backup tool to download all the files it recorded in fileids?
The method I'm using is C#/.NET https://developers.google.com/drive/v3/web/manage-downloads#examples
I'll spare the boring details and say that part of my program logs in Once as each user (well, using the Apps Service API), grabs all their files' fileIds and records them to a flat text file. My program then opens that flat text file and begins downloading each fileId recorded for that user, but the problem is: it's soooo slow because it opens a new connection for a file, waits for the file to finish, then gets a new fileid and starts the whole process over again. It's not very efficient.
Google's example, which I copied pretty much Verbatim (I modified the vars a little bit by immediately grabbing and exporting their mimetype, so the first 3 lines are moot):
var fileId = "0BwwA4oUTeiV1UVNwOHItT0xfa2M";
var request = driveService.Files.Get(fileId);
var stream = new System.IO.MemoryStream();
// Add a handler which will be notified on progress changes.
// It will notify on each chunk download and when the
// download is completed or failed.
request.MediaDownloader.ProgressChanged +=
(IDownloadProgress progress) =>
{
switch(progress.Status)
{
case DownloadStatus.Downloading:
{
Console.WriteLine(progress.BytesDownloaded);
break;
}
case DownloadStatus.Completed:
{
Console.WriteLine("Download complete.");
break;
}
case DownloadStatus.Failed:
{
Console.WriteLine("Download failed.");
break;
}
}
};
request.Download(stream);
Is there Any way I can streamline this so that my program can download all the files it knows for the user in one big handshake, vs reading a fileid individually, then opening a session, exporting, downloading, closing, then doing the same exact thing for the next file? Hope this makes sense.
Thank you for any help ahead of time!
--Mike
---EDIT---
I wanted to add more details so that hopefully what I'm looking to do makes more sense:
So what's happening in the following code is: I am creating a "request" that will let me export the filetype (which I have from the flat text file as the fileId[0], and the "mimetype" which is in the array as fileId[1].)
What's killing the speed of the program is having to build the "BuildService" request each time for each file.
foreach (var file in deltafiles)
{
try
{
if (bgW.CancellationPending)
{
stripLabel.Text = "Backup canceled!";
e.Cancel = true;
break;
}
DateTime dt = DateTime.Now;
string[] foldervalues = File.ReadAllLines(savelocation + "folderlog.txt");
cnttototal++;
bgW.ReportProgress(cnttototal);
// Our file is a CSV. Column 1 = file ID, Column 2 = File name
var values = file.Split(',');
string fileId = values[0];
string fileName = values[1];
string mimetype = values[2];
mimetype = mimetype.Replace(",", "_");
string folder = values[3];
int foundmatch = 0;
int folderfilelen = foldervalues.Count();
fileName = fileName.Replace('\\', '_').Replace('/', '_').Replace(':', '_').Replace('!', '_').Replace('\'', '_').Replace('*', '_').Replace('#', '_').Replace('[', '_').Replace(']', '_');
var request = CreateService.BuildService(user).Files.Export(fileId, mimetype);
//Default extensions for files. Not sure what this should be, so we'll null it for now.
string ext = null;
// Things get sloppy here. The reason we're checking MimeTypes
// is because we have to export the files from Google's format
// to a format that is readable by a desktop computer program
// So for example, the google-apps.spreadsheet will become an MS Excel file.
if (mimetype == mimeSheet || mimetype == mimeSheetRitz || mimetype == mimeSheetml)
{
request = CreateService.BuildService(user).Files.Export(fileId, exportSheet);
ext = ".xls";
}
if (mimetype == mimeDoc || mimetype == mimeDocKix || mimetype == mimeDocWord)
{
request = CreateService.BuildService(user).Files.Export(fileId, exportDoc);
ext = ".docx";
}
if (mimetype == mimePres || mimetype == mimePresPunch)
{
request = CreateService.BuildService(user).Files.Export(fileId, exportPres);
ext = ".ppt";
}
if (mimetype == mimeForm || mimetype == mimeFormfb || mimetype == mimeFormDrawing)
{
request = CreateService.BuildService(user).Files.Export(fileId, exportForm);
ext = ".docx";
}
// Any other file type, assume as know what it is (which in our case, will be a txt file)
// apply the mime type and carry on.
string dest = Path.Combine(savelocation, fileName + ext);
var stream = new System.IO.FileStream(dest, FileMode.Create, FileAccess.ReadWrite);
int oops = 0;
// Add a handler which will be notified on progress changes.
// It will notify on each chunk download and when the
// download is completed or failed.
request.MediaDownloader.ProgressChanged +=
(IDownloadProgress progress) =>
{
switch (progress.Status)
{
case DownloadStatus.Downloading:
{
throw new Exception("File may be corrupted.");
break;
}
case DownloadStatus.Completed:
{
Console.WriteLine("Download complete.");
break;
}
case DownloadStatus.Failed:
{
oops = 1;
logFile.WriteLine(fileName + " could not be downloaded. Possible Google draw/form OR bad name.\n");
break;
}
}
};
request.Download(stream);
stream.Close();
stream.Dispose();
Is there any way I could streamline this process so I don't have to build the drive service Every time I want to download a file? The flat text file the program reads looks similar to
FILEID,ACTUAL FILE NAME,MIMETYPE
So is there any way I could cut out the middle man and feed the request.Download method without constantly reminding the "foreach" statement to export the file type as a file system-readable file? (good grief, sorry, I know this sounds like a lot.)
Any pointers would be great!!
You might want to try the tutorial : Google Drive API with C# .net – Download. This is a much simpler code to download a file. Also there are other factors like intermittent internet connect that may affect the ETA of downloading the file.
Code Sample :
/// Download a file
/// Documentation: https://developers.google.com/drive/v2/reference/files/get
///
/// a Valid authenticated DriveService
/// File resource of the file to download
/// location of where to save the file including the file name to save it as.
///
public static Boolean downloadFile(DriveService _service, File _fileResource, string _saveTo)
{
if (!String.IsNullOrEmpty(_fileResource.DownloadUrl))
{
try
{
var x = _service.HttpClient.GetByteArrayAsync(_fileResource.DownloadUrl );
byte[] arrBytes = x.Result;
System.IO.File.WriteAllBytes(_saveTo, arrBytes);
return true;
}
catch (Exception e)
{
Console.WriteLine("An error occurred: " + e.Message);
return false;
}
}
else
{
// The file doesn't have any content stored on Drive.
return false;
}
}
Using _service.HttpClient.GetByteArrayAsync we can pass it the download url of the file we would like to download. Once the file is download its a simple matter of wright the file to the disk.
Hope this helps!
This isn't an answer as much as it is a work around, even then it's only half the answer (for right now.) I threw my gloves off and played dirty.
First, I updated my nuget google api packages to the latest version available today inside my VS project, then went to https://github.com/google/google-api-dotnet-client, forked/cloned it, changed the Google.Apis.Drive.v3.cs file (which compiles to google.apis.drive.v3.dll) so that the mimetype is no longer read only (it can do get; and set;, when by default, it only allowed get).
Because I already knew the mime types, I am able to force assign the mime type now to the request and go on with my life, instead of having to build the client service, connect, only to export the file type that I already know it is.
It's not pretty, not how it should be done, but this was really bothering me!
Going back to #Mr.Rebot, I thank you again for your help and research! :-)
I'm trying to download file (image ) using my bot, but when I download the image ( which is done successfully ) after using getFile, the image I received is very small 1.7 kb while it's bigger than that on my mobile phone
This is an old post. But since there is not a good documentation on how you should download file in telegram bot, for anyone wondering, that's how you should do it(One way of it):
DownloadFile(message.Photo[message.Photo.Length - 1].FileId, #"c:\photo.jpg");
in which:
private static async void DownloadFile(string fileId, string path)
{
try
{
var file = await Bot.GetFileAsync(fileId);
using (var saveImageStream = new FileStream(path, FileMode.Create))
{
await file.FileStream.CopyToAsync(saveImageStream);
}
}
catch (Exception ex)
{
Console.WriteLine("Error downloading: " + ex.Message);
}
}
The message.Photo[message.Photo.Length - 1] is the last element in message.Photo array, which contains the highest quality image data. Obviously you can use DownloadFile to download other kind of files(for example message.Document) as well.
the getFile Method present a JSON object (the 1.7 KB response) that contain the data for accessing your image file.
also note that telegram create an array of image for any image. the first element of this array contain the small thumbnail of your original image and the latest element of the array contain your original image.
var file = await Bot.GetFileAsync(message.Document.FileId);
FileStream fs=new FileStream("Your Destination Path And File Name",FileMode.Create);
await Bot.DownloadFileAsync(file.FilePath, fs);
fs.Close();
fs.Dispose();
I use telegram.bot v14.10.0 but I can't find file.FileStream so I find alternative way to get image from telegram. my way is to use telegram api directly for this case.
var test = _myBot.GetFileAsync(e.Message.Photo[e.Message.Photo.Count() - 1].FileId);
var download_url = #"https://api.telegram.org/file/bot<token>/" + test.Result.FilePath;
using (WebClient client = new WebClient())
{
client.DownloadFile(new Uri(download_url), #"c:\temp\NewCompanyPicure.png");
}
//then do what you want with it
You need use await botClient.DownloadFileAsync(file.FilePath, saveImageStream); instead await file.FileStream.CopyToAsync(saveImageStream);
Your code should look like this:
static async void DownloadFile(ITelegramBotClient botClient, string fileId, string path)
{
try
{
var file = await botClient.GetFileAsync(fileId);
using (var saveImageStream = new FileStream(path, FileMode.Create))
{
await botClient.DownloadFileAsync(file.FilePath, saveImageStream);
}
}
catch (Exception ex)
{
Console.WriteLine("Error downloading: " + ex.Message);
}
}
Telegram.Bot from version 14.2.0 commit in examples: https://github.com/TelegramBots/Telegram.Bot.Examples/commit/ff5a44133ad3b0d3c1e4a8b82edce959d0ee0d0e