DropBox API calls Uploading a Zero byte file - c#

I am trying to upload a file to dropbox using rest calls but it is not uploading the actual file, it is uploading a zero byte file.
Please check the code and let me know if i am missing something.
var task = Task.Run((Func<Task<int>>)OrderExtractUsecase.DropBox);
task.Wait();
int x = task.Result;
Dropbox task Code is:
static async Task<int> DropBox()
{
try
{
Dropbox_Utility objDropBox = new Dropbox_Utility("<accessid>");
foreach (string temp in fileList)
{
await objDropBox.Upload("/Assist", temp);
}
return 1;
}
catch(Exception ex)
{
return -1;
}
}
Upload task code is:
public async Task<string> Upload(string folder, string filefullpath)
{
string filename = string.Empty;
string fileID = string.Empty;
try
{
filename = Path.GetFileName(filefullpath);
using (FileStream fileStream = File.OpenRead(filefullpath))
{
using (MemoryStream memoryStream = new MemoryStream())
{
fileStream.CopyTo(memoryStream);
var response = await dbx.Files.UploadAsync(folder + "/" + filename, WriteMode.Overwrite.Instance, body: memoryStream);
fileID = response.Id;
}
}
}
catch (Exception ex) { throw; }
finally { }
return fileID;
}

As awh112 mentioned, you need to reset the position of memoryStream. After the copyTo, the Position of memoryStream is the length of the file. For that reason, your code will upload a zero byte file. I've confirmed as much with the following:
fileStream.CopyTo(memoryStream);
Console.WriteLine(memoryStream.Position);
var response = await dbx.Files.UploadAsync(folder + "/" + filename, WriteMode.Overwrite.Instance, body: memoryStream);
Console.WriteLine((response as FileMetadata).Size);
That prints: (in my case, my test file is just 12 bytes long)
12
0
You can rewind it like this:
fileStream.CopyTo(memoryStream);
Console.WriteLine(memoryStream.Position);
memoryStream.Position = 0;
Console.WriteLine(memoryStream.Position);
var response = await dbx.Files.UploadAsync(folder + "/" + filename, WriteMode.Overwrite.Instance, body: memoryStream);
Console.WriteLine((response as FileMetadata).Size);
That prints:
12
0
12
The resulting uploaded file then contains the expected contents.

Related

Problem converting Image to Base64 in Blazor Server

So I am trying to convert an image base64 that will be uploaded to SQL Server.
Current code is:
private async Task OnInputFileChange(InputFileChangeEventArgs args)
{
var maxFiles = 1;
var maxSize = 512000000;
var format = "image/jpg";
test = "Something";
test1 = args.FileCount.ToString();
foreach (var file in args.GetMultipleFiles(maxFiles))
{
var image = await file.RequestImageFileAsync(format, 500, 500);
test = image.Size.ToString();
buffer = new byte[image.Size];
await image.OpenReadStream(maxAllowedSize: maxSize).ReadAsync(buffer);
test1 = buffer.ToString();
var imageDataUrl = $"data:{format};base64,{Convert.ToBase64String(buffer)}";
imageDataUrls.Add(imageDataUrl);
imageString = imageDataUrl;
}
}
It begins fine, however only the top portion of image is actually converted and in the string is followed by thousands of repeating "A". Reconstructing the image just shows the top portion of the image. What am I doing wrong?
Currently I had not uploaded and redownloaded the string, it is all local until I can figure out what is wrong. I am using the imageString for the image source. I am using .net 6.0.
Try this. Works for me. Files since 1 MB.
public static byte[] GetBytes(Stream stream)
{
var bytes = new byte[stream.Length];
stream.Seek(0, SeekOrigin.Begin);
stream.ReadAsync(bytes, 0, bytes.Length);
stream.Dispose();
return bytes;
}
private async Task OnInputFileChange(InputFileChangeEventArgs args)
{
string base64String = "";
try
{
var files = args.GetMultipleFiles();
foreach (var file in files)
{
await using MemoryStream fs = new MemoryStream();
await file.OpenReadStream(maxAllowedSize: 1048576).CopyToAsync(fs);
byte[] somBytes = GetBytes(fs);
base64String = Convert.ToBase64String(somBytes, 0, somBytes.Length);
System.Diagnostics.Debug.Print("Imatge 64: " + base64String + Environment.NewLine);
}
}
catch (Exception e)
{
System.Diagnostics.Debug.Print("ERROR: " + e.Message + Environment.NewLine);
}
}

Save byte as excel file on the server side

I have a method that returns a stream which will be written as an excel file on the clientside
Before getting to the client side I will like to write the file to a path and make sure
it is working.
How can I write bytes to a path on the c drive?
public async Task<IActionResult> GetTaxCodeMap(int fileId)
{
var result = await _myFileService.GetMyData(fileId);
if (result == null)
{
return NotFound("File does not exist.");
}
using (var memoryStream = new MemoryStream())
{
result.CopyTo(memoryStream);
var path = #"Workbk.xlsx";
var bytes = memoryStream.ToArray();
File.WriteAllBytes(path, bytes); //? Not working
//return File(memoryStream.ToArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", $"Workbk.xlsx");
}
return ok
}

Download PNG file to local machine using Dropbox.NET on UWP platform

I am able to upload PNG image to Dropbox folder however, I don't know how to download the PNG (or other images) from Dropbox. What I get from the tutorial page is:
async Task Download(DropboxClient dbx, string folder, string file)
{
using (var response = await dbx.Files.DownloadAsync(folder + "/" + file))
{
Console.WriteLine(await response.GetContentAsStringAsync());
}
}
Do anyone have the sample code for downloading file to local drive? Thanks.
After some findings and tryings, finally I found the solution:
public static async Task Download(string folder, string file)
{
StorageFolder storeFolder = ApplicationData.Current.LocalFolder;
CreationCollisionOption options = CreationCollisionOption.ReplaceExisting;
StorageFile outputFile = await storeFolder.CreateFileAsync("temp.png", options);
using (var dbx = new DropboxClient(yourAccessToken))
{
var response = await dbx.Files.DownloadAsync(downloadFolder);
{
using (var file = await outputFile.OpenStreamForWriteAsync())
{
Stream imageStream = await response.GetContentAsStreamAsync();
CopyStream(imageStream, file);
}
}
}
}
With a helper function:
public static void CopyStream(Stream input, Stream output)
{
byte[] buffer = new byte[8 * 1024];
int len;
while ((len = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, len);
}
}
To upload file:
public static async Task Upload(string filename, string filePath)
{
try
{
string TargetPath = "/data/" + filename + ".png";
const int ChunkSize = 4096 * 1024;
using (var dbx = new DropboxClient(yourAccessToken))
{
using (var fileStream = File.Open(filePath, FileMode.Open))
{
if (fileStream.Length <= ChunkSize)
{
await dbx.Files.UploadAsync(TargetPath, null, false, null, false, body: fileStream);
}
else
{
MessageDialog dialog = new MessageDialog("File is too big");
await dialog.ShowAsync();
}
}
}
}
catch (Exception ex)
{
MessageDialog dialog = new MessageDialog("Error uploading file. " + ex.Message);
await dialog.ShowAsync();
}
}
Hope it helps. Thanks.
The Dropbox API .NET SDK DownloadAsync method gives you a IDownloadResponse which offers three methods for getting the file content:
GetContentAsByteArrayAsync
GetContentAsStreamAsync
GetContentAsStringAsync
For example, to save the file content to a local file, you can do something like:
public async Task Download(string remoteFilePath, string localFilePath)
{
using (var response = await client.Files.DownloadAsync(remoteFilePath))
{
using (var fileStream = File.Create(localFilePath))
{
response.GetContentAsStreamAsync().Result.CopyTo(fileStream);
}
}
}
That would download the file content from a file at remote Dropbox file path remoteFilePath to the local path localFilePath.
What #Greg said is correct. I would like to make a small change that localFilePath mentioned in the code, should have an extension also. For example it should be something like C:\Code\image.jgp and not something like C:\Code. If the filelocation specified does not exist, it will be created automatically and this code will work perfectly.
public async Task Download(string remoteFilePath, string localFilePath)
{
using (var response = await client.Files.DownloadAsync(remoteFilePath))
{
using (var fileStream = File.Create(localFilePath))
{
response.GetContentAsStreamAsync().Result.CopyTo(fileStream);
}
}
}
Here is my logic to download a file using Dropbox.Net API:
private async Task Download(DropboxClient dbx, string remoteFilePath, string localFilePath) {
using(var response = await dbx.Files.DownloadAsync(remoteFilePath)) {
var fileSize = response.Response.Size;
var bufferSize = 1024 * 1024;
var buffer = new byte[bufferSize];
using(var stream = await response.GetContentAsStreamAsync()) {
using(System.IO.FileStream file = new System.IO.FileStream(localFilePath, FileMode.OpenOrCreate)) {
var length = stream.Read(buffer, 0, bufferSize);
while (length > 0) {
file.Write(buffer, 0, length);
var percentage = 100 * file.Length / (double) fileSize;
Console.WriteLine(percentage);
length = stream.Read(buffer, 0, bufferSize);
}
}
}
}
}
And you can call it like:
Await(Download(dbx, url, #"yourDestinationFolder\" + item.Name));
Where item.Name is the full name of the downloaded file e.g. setup.exe

Unable to delete a file with DeleteAsync

I'm working in VS2012, WinRT and C#.
I'm trying to delete some files after decompressing them. I'm getting an "Access is denied" error. If I stop the app and re-start it the same code works fine so it appears there is a handle still attached.
If I don't call the unZipFile method, I can delete the files.
Is there a definitive way to release a file? I've set it to null (file = null;) before the call to delete.
Here's the block of code that calls the unzip method:
StorageFile file = await CreateOutputFile(fileName, path);
MemoryStream theMemStream = new MemoryStream();
theMemStream.Write(bytes, 0, bytes.Length);
await FileIO.WriteBytesAsync(file, bytes);
await theMemStream.FlushAsync();
theMemStream.Dispose();
var result = await unZipFile(file, path);
file = null;
Here's the unZipFile method:
private async Task<string> unZipFile(StorageFile file, string path)
{
StorageFolder sf = await GetOutputFolder(path);
using (var zipStream = await file.OpenStreamForReadAsync())
{
using (MemoryStream zipMemoryStream = new MemoryStream((int)zipStream.Length))
{
await zipStream.CopyToAsync(zipMemoryStream);
try
{
var archive = SharpCompress.Archive.ArchiveFactory.Open(file.Path);
foreach (var entry in archive.Entries)
{
entry.WriteTo(zipMemoryStream);
Stream fileData = entry.OpenEntryStream();
StorageFile outputFile = await sf.CreateFileAsync(entry.FilePath, CreationCollisionOption.ReplaceExisting);
using (Stream outputFileStream = await outputFile.OpenStreamForWriteAsync())
{
await fileData.CopyToAsync(outputFileStream);
await outputFileStream.FlushAsync();
outputFileStream.Dispose();
}
}
archive = null;
}
catch (Exception ex)
{
throw new IOException("Error writing decompressed output file: " + ex.Message);
}
await zipStream.FlushAsync();
zipStream.Dispose();
await zipMemoryStream.FlushAsync();
zipMemoryStream.Dispose();
}
}
return "success";
}
Here's the delete method. This is called for each file after decompression:
private async Task<string> deleteFile(string path, string filename)
{
StorageFolder folder = await GetOutputFolder(path);
var files = await folder.GetFilesAsync();
foreach (StorageFile file in files)
{
try
{
if (file != null)
{
if (file.Name == filename)
await file.DeleteAsync();
}
}
catch (Exception ex)
{
return ex.Message;
}
}
return "success";
}
On what file do you get the exception, the extracted files or the zip archive itself?
If the latter is the case, ArchiveFactory.Open() returns an IArchive which inherits IDisposable, so you should wrap var archive = SharpCompress.Archive.ArchiveFactory.Open(file.Path); in a using block so it gets disposed after use.

How To Accept a File POST

I'm using asp.net mvc 4 webapi beta to build a rest service. I need to be able to accept POSTed images/files from client applications. Is this possible using the webapi? Below is how action I am currently using. Does anyone know of an example how this should work?
[HttpPost]
public string ProfileImagePost(HttpPostedFile profileImage)
{
string[] extensions = { ".jpg", ".jpeg", ".gif", ".bmp", ".png" };
if (!extensions.Any(x => x.Equals(Path.GetExtension(profileImage.FileName.ToLower()), StringComparison.OrdinalIgnoreCase)))
{
throw new HttpResponseException("Invalid file type.", HttpStatusCode.BadRequest);
}
// Other code goes here
return "/path/to/image.png";
}
I'm surprised that a lot of you seem to want to save files on the server. Solution to keep everything in memory is as follows:
[HttpPost("api/upload")]
public async Task<IHttpActionResult> Upload()
{
if (!Request.Content.IsMimeMultipartContent())
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
var provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
foreach (var file in provider.Contents)
{
var filename = file.Headers.ContentDisposition.FileName.Trim('\"');
var buffer = await file.ReadAsByteArrayAsync();
//Do whatever you want with filename and its binary data.
}
return Ok();
}
see http://www.asp.net/web-api/overview/formats-and-model-binding/html-forms-and-multipart-mime#multipartmime, although I think the article makes it seem a bit more complicated than it really is.
Basically,
public Task<HttpResponseMessage> PostFile()
{
HttpRequestMessage request = this.Request;
if (!request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(HttpStatusCode.UnsupportedMediaType);
}
string root = System.Web.HttpContext.Current.Server.MapPath("~/App_Data/uploads");
var provider = new MultipartFormDataStreamProvider(root);
var task = request.Content.ReadAsMultipartAsync(provider).
ContinueWith<HttpResponseMessage>(o =>
{
string file1 = provider.BodyPartFileNames.First().Value;
// this is the file name on the server where the file was saved
return new HttpResponseMessage()
{
Content = new StringContent("File uploaded.")
};
}
);
return task;
}
See the code below, adapted from this article, which demonstrates the simplest example code I could find. It includes both file and memory (faster) uploads.
public HttpResponseMessage Post()
{
var httpRequest = HttpContext.Current.Request;
if (httpRequest.Files.Count < 1)
{
return Request.CreateResponse(HttpStatusCode.BadRequest);
}
foreach(string file in httpRequest.Files)
{
var postedFile = httpRequest.Files[file];
var filePath = HttpContext.Current.Server.MapPath("~/" + postedFile.FileName);
postedFile.SaveAs(filePath);
// NOTE: To store in memory use postedFile.InputStream
}
return Request.CreateResponse(HttpStatusCode.Created);
}
The ASP.NET Core way is now here:
[HttpPost("UploadFiles")]
public async Task<IActionResult> Post(List<IFormFile> files)
{
long size = files.Sum(f => f.Length);
// full path to file in temp location
var filePath = Path.GetTempFileName();
foreach (var formFile in files)
{
if (formFile.Length > 0)
{
using (var stream = new FileStream(filePath, FileMode.Create))
{
await formFile.CopyToAsync(stream);
}
}
}
// process uploaded files
// Don't rely on or trust the FileName property without validation.
return Ok(new { count = files.Count, size, filePath});
}
Here is a quick and dirty solution which takes uploaded file contents from the HTTP body and writes it to a file. I included a "bare bones" HTML/JS snippet for the file upload.
Web API Method:
[Route("api/myfileupload")]
[HttpPost]
public string MyFileUpload()
{
var request = HttpContext.Current.Request;
var filePath = "C:\\temp\\" + request.Headers["filename"];
using (var fs = new System.IO.FileStream(filePath, System.IO.FileMode.Create))
{
request.InputStream.CopyTo(fs);
}
return "uploaded";
}
HTML File Upload:
<form>
<input type="file" id="myfile"/>
<input type="button" onclick="uploadFile();" value="Upload" />
</form>
<script type="text/javascript">
function uploadFile() {
var xhr = new XMLHttpRequest();
var file = document.getElementById('myfile').files[0];
xhr.open("POST", "api/myfileupload");
xhr.setRequestHeader("filename", file.name);
xhr.send(file);
}
</script>
I used Mike Wasson's answer before I updated all the NuGets in my webapi mvc4 project. Once I did, I had to re-write the file upload action:
public Task<HttpResponseMessage> Upload(int id)
{
HttpRequestMessage request = this.Request;
if (!request.Content.IsMimeMultipartContent())
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.UnsupportedMediaType));
}
string root = System.Web.HttpContext.Current.Server.MapPath("~/App_Data/uploads");
var provider = new MultipartFormDataStreamProvider(root);
var task = request.Content.ReadAsMultipartAsync(provider).
ContinueWith<HttpResponseMessage>(o =>
{
FileInfo finfo = new FileInfo(provider.FileData.First().LocalFileName);
string guid = Guid.NewGuid().ToString();
File.Move(finfo.FullName, Path.Combine(root, guid + "_" + provider.FileData.First().Headers.ContentDisposition.FileName.Replace("\"", "")));
return new HttpResponseMessage()
{
Content = new StringContent("File uploaded.")
};
}
);
return task;
}
Apparently BodyPartFileNames is no longer available within the MultipartFormDataStreamProvider.
Toward this same directions, I'm posting a client and server snipets that send Excel Files using WebApi, c# 4:
public static void SetFile(String serviceUrl, byte[] fileArray, String fileName)
{
try
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
using (var content = new MultipartFormDataContent())
{
var fileContent = new ByteArrayContent(fileArray);//(System.IO.File.ReadAllBytes(fileName));
fileContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = fileName
};
content.Add(fileContent);
var result = client.PostAsync(serviceUrl, content).Result;
}
}
}
catch (Exception e)
{
//Log the exception
}
}
And the server webapi controller:
public Task<IEnumerable<string>> Post()
{
if (Request.Content.IsMimeMultipartContent())
{
string fullPath = HttpContext.Current.Server.MapPath("~/uploads");
MyMultipartFormDataStreamProvider streamProvider = new MyMultipartFormDataStreamProvider(fullPath);
var task = Request.Content.ReadAsMultipartAsync(streamProvider).ContinueWith(t =>
{
if (t.IsFaulted || t.IsCanceled)
throw new HttpResponseException(HttpStatusCode.InternalServerError);
var fileInfo = streamProvider.FileData.Select(i =>
{
var info = new FileInfo(i.LocalFileName);
return "File uploaded as " + info.FullName + " (" + info.Length + ")";
});
return fileInfo;
});
return task;
}
else
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.NotAcceptable, "Invalid Request!"));
}
}
And the Custom MyMultipartFormDataStreamProvider, needed to customize the Filename:
PS: I took this code from another post http://www.codeguru.com/csharp/.net/uploading-files-asynchronously-using-asp.net-web-api.htm
public class MyMultipartFormDataStreamProvider : MultipartFormDataStreamProvider
{
public MyMultipartFormDataStreamProvider(string path)
: base(path)
{
}
public override string GetLocalFileName(System.Net.Http.Headers.HttpContentHeaders headers)
{
string fileName;
if (!string.IsNullOrWhiteSpace(headers.ContentDisposition.FileName))
{
fileName = headers.ContentDisposition.FileName;
}
else
{
fileName = Guid.NewGuid().ToString() + ".data";
}
return fileName.Replace("\"", string.Empty);
}
}
[HttpPost]
public JsonResult PostImage(HttpPostedFileBase file)
{
try
{
if (file != null && file.ContentLength > 0 && file.ContentLength<=10485760)
{
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/") + "HisloImages" + "\\", fileName);
file.SaveAs(path);
#region MyRegion
////save imag in Db
//using (MemoryStream ms = new MemoryStream())
//{
// file.InputStream.CopyTo(ms);
// byte[] array = ms.GetBuffer();
//}
#endregion
return Json(JsonResponseFactory.SuccessResponse("Status:0 ,Message: OK"), JsonRequestBehavior.AllowGet);
}
else
{
return Json(JsonResponseFactory.ErrorResponse("Status:1 , Message: Upload Again and File Size Should be Less Than 10MB"), JsonRequestBehavior.AllowGet);
}
}
catch (Exception ex)
{
return Json(JsonResponseFactory.ErrorResponse(ex.Message), JsonRequestBehavior.AllowGet);
}
}
This question has lots of good answers even for .Net Core. I was using both Frameworks the provided code samples work fine. So I won't repeat it. In my case the important thing was how to use File upload actions with Swagger like this:
Here is my recap:
ASP .Net WebAPI 2
To upload file use: MultipartFormDataStreamProvider see answers here
How to use it with Swagger
.NET Core
To upload file use: IFormFile see answers here or MS documentation
How to use it with Swagger
Here are two ways to accept a file. One using in memory provider MultipartMemoryStreamProvider and one using MultipartFormDataStreamProvider which saves to a disk. Note, this is only for one file upload at a time. You can certainty extend this to save multiple-files. The second approach can support large files. I've tested files over 200MB and it works fine. Using in memory approach does not require you to save to disk, but will throw out of memory exception if you exceed a certain limit.
private async Task<Stream> ReadStream()
{
Stream stream = null;
var provider = new MultipartMemoryStreamProvider();
await Request.Content.ReadAsMultipartAsync(provider);
foreach (var file in provider.Contents)
{
var buffer = await file.ReadAsByteArrayAsync();
stream = new MemoryStream(buffer);
}
return stream;
}
private async Task<Stream> ReadLargeStream()
{
Stream stream = null;
string root = Path.GetTempPath();
var provider = new MultipartFormDataStreamProvider(root);
await Request.Content.ReadAsMultipartAsync(provider);
foreach (var file in provider.FileData)
{
var path = file.LocalFileName;
byte[] content = File.ReadAllBytes(path);
File.Delete(path);
stream = new MemoryStream(content);
}
return stream;
}
I had a similar problem for the preview Web API. Did not port that part to the new MVC 4 Web API yet, but maybe this helps:
REST file upload with HttpRequestMessage or Stream?
Please let me know, can sit down tomorrow and try to implement it again.
API Controller :
[HttpPost]
public HttpResponseMessage Post()
{
var httpRequest = System.Web.HttpContext.Current.Request;
if (System.Web.HttpContext.Current.Request.Files.Count < 1)
{
//TODO
}
else
{
try
{
foreach (string file in httpRequest.Files)
{
var postedFile = httpRequest.Files[file];
BinaryReader binReader = new BinaryReader(postedFile.InputStream);
byte[] byteArray = binReader.ReadBytes(postedFile.ContentLength);
}
}
catch (System.Exception e)
{
//TODO
}
return Request.CreateResponse(HttpStatusCode.Created);
}
Complementing Matt Frear's answer - This would be an ASP NET Core alternative for reading the file directly from Stream, without saving&reading it from disk:
public ActionResult OnPostUpload(List<IFormFile> files)
{
try
{
var file = files.FirstOrDefault();
var inputstream = file.OpenReadStream();
XSSFWorkbook workbook = new XSSFWorkbook(stream);
var FIRST_ROW_NUMBER = {{firstRowWithValue}};
ISheet sheet = workbook.GetSheetAt(0);
// Example: var firstCellRow = (int)sheet.GetRow(0).GetCell(0).NumericCellValue;
for (int rowIdx = 2; rowIdx <= sheet.LastRowNum; rowIdx++)
{
IRow currentRow = sheet.GetRow(rowIdx);
if (currentRow == null || currentRow.Cells == null || currentRow.Cells.Count() < FIRST_ROW_NUMBER) break;
var df = new DataFormatter();
for (int cellNumber = {{firstCellWithValue}}; cellNumber < {{lastCellWithValue}}; cellNumber++)
{
//business logic & saving data to DB
}
}
}
catch(Exception ex)
{
throw new FileFormatException($"Error on file processing - {ex.Message}");
}
}

Categories