I'm having trouble downloading a file with content using a Powerpoint presentation library (Syncfusion). The docs only supply ASP examples, no Web API specifically.
I can save a file to the file system which has the context I add to the Powerpoint.
I can get the API to download a file to the users browser but this Powerpoint is empty.
Syncfusion has a function to save the Powerpoint to a memory stream so I guess my question is what is the correct way to save a file to the users browser with the content from the stream?
I'm using HTTPGet and hitting the link through the browser. Do I need to sent the context-type or anything like that?
Thanks for your help, I can provide what I have so far if that helps.
Kurtis
Edit:
[HttpGet, Route("")]
public HttpResponseMessage Get()
{
var presentation = Presentation.Create();
var firstSlide = presentation.Slides.Add(SlideLayoutType.Blank);
var textShape = firstSlide.AddTextBox(100, 75, 756, 200);
var paragraph = textShape.TextBody.AddParagraph();
paragraph.HorizontalAlignment = HorizontalAlignmentType.Center;
var textPart = paragraph.AddTextPart("Kurtis' Presentation");
textPart.Font.FontSize = 80;
var memoryStream = new MemoryStream();
presentation.Save(memoryStream);
var result = Request.CreateResponse(HttpStatusCode.OK);
result.Content = new StreamContent(memoryStream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "export.pptx"
};
return result;
}
This is what I have, the library saves the presentation to the memory stream, you can change the parameter to a string which writes this to a file and there is an option to pass a filename, format and HttpResponse for MVC but I couldn't get this working with my API controller. I kept getting a network error but didn't know why.
Thanks again
I found my problem with the help of others in the comments.
The code above work but I needed to reset the stream position to zero to actually write the data.
memoryStream.Position = 0;
Thanks for those who commented. :-)
Related
I'm using latest and recommended Azure.Storage.Blobs package. I'm uploading the video file as chunks, which works fine. The problem is now returning back the video to the web client, which is videojs. The player is using Range request.
My endpoint:
[HttpGet]
[Route("video/{id}")]
[AllowAnonymous]
public async Task<IActionResult> GetVideoStreamAsync(string id)
{
var stream = await GetVideoFile(id);
return File(stream, "video/mp4", true); // true is for enableRangeProcessing
}
And my GetVideoFile method
var ms = new MemoryStream();
await blobClient.DownloadToAsync(ms, null, new StorageTransferOptions
{
InitialTransferLength = 1024 * 1024,
MaximumConcurrency = 20,
MaximumTransferLength = 4 * 1024 * 1024
});
ms.Position = 0;
return ms;
The video gets downloaded and streamed just fine. But it downloads the whole video and not respecting Range at all. I've also tried with DownloadTo(HttpRange)
var ms = new MemoryStream();
// parse range header...
var range = new HttpRange(from, to);
BlobDownloadInfo info = await blobClient.DownloadAsync(range);
await info.Content.CopyToAsync(ms);
return ms;
But nothing gets displayed in the browser. What is the best way to achieve that?
Answering my own question if someone comes across.
CloudBlockBlob (version I'm using: 11.2.2) now has OpenReadAsync() method which returns stream. In my case I'm returning this stream to videojs which handles the Range header on its own.
Please try by resetting the memory stream's position to 0 before returning:
var ms = new MemoryStream();
// parse range header...
var range = new HttpRange(from, to);
BlobDownloadInfo info = await blobClient.DownloadAsync(range);
await info.Content.CopyToAsync(ms);
ms.Position = 0;//ms is positioned at the end of the stream so we need to reset that.
return ms;
I believe it's not possible to achieve it only using Azure Blob. More info in here: https://stackoverflow.com/a/26053910/1384539
but in summary, you can use a CDN that offers the Seek Start / End position : https://docs.vdms.com/cdn/re3/Content/Streaming/HPD/Seeking_Within_a_Video.htm
Another possibility is to use Azure Media Services that supports streamming. Your approach is actually a progressive download which is not exactly the same idea, and you'd probably spend a lot with network out. (assuming you have many access to the same file)
I have a Web Api controller method that gets passed document IDs and it should return the document files individually for those requested Ids. I have tried the accepted answer from the following link to achieve this functionality, but it's not working. I don't know where I did go wrong.
What's the best way to serve up multiple binary files from a single WebApi method?
My Web Api Method,
public async Task<HttpResponseMessage> DownloadMultiDocumentAsync(
IClaimedUser user, string documentId)
{
List<long> docIds = documentId.Split(',').Select(long.Parse).ToList();
List<Document> documentList = coreDataContext.Documents.Where(d => docIds.Contains(d.DocumentId) && d.IsActive).ToList();
var content = new MultipartContent();
CloudBlockBlob blob = null;
var container = GetBlobClient(tenantInfo);
var directory = container.GetDirectoryReference(
string.Format(DirectoryNameConfigValue, tenantInfo.TenantId.ToString(), documentList[0].ProjectId));
for (int docId = 0; docId < documentList.Count; docId++)
{
blob = directory.GetBlockBlobReference(DocumentNameConfigValue + documentList[docId].DocumentId);
if (!blob.Exists()) continue;
MemoryStream memStream = new MemoryStream();
await blob.DownloadToStreamAsync(memStream);
memStream.Seek(0, SeekOrigin.Begin);
var streamContent = new StreamContent(memStream);
content.Add(streamContent);
}
HttpResponseMessage httpResponseMessage = new HttpResponseMessage();
httpResponseMessage.Content = content;
httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
httpResponseMessage.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
httpResponseMessage.StatusCode = HttpStatusCode.OK;
return httpResponseMessage;
}
I tried with 2 or more document Ids but only one file was downloaded and that also is not in the correct format (without extension).
Zipping is the only option that will have consistent result on all browsers. MIME/multipart content is for email messages (https://en.wikipedia.org/wiki/MIME#Multipart_messages) and it was never intended to be received and parsed on the client side of a HTTP transaction. Some browsers do implement it, some others don't.
Alternatively, you can change your API to take in a single docId and iterate over your API from your client for each docId.
I think only way is that you zip your all the files and then download one zip file. I guess you can use dotnetzip package because it is easy to use.
One way is that, you can first save your files on disk and then stream the zip to download. Another way is, you can zip them in memory and then download the file in stream
public ActionResult Download()
{
using (ZipFile zip = new ZipFile())
{
zip.AddDirectory(Server.MapPath("~/Directories/hello"));
MemoryStream output = new MemoryStream();
zip.Save(output);
return File(output, "application/zip", "sample.zip");
}
}
I have images stored in SQL server, and I want to send them to Android app via JSON with other data. What's the best way to do this?
BTW my server side written in ASP Web API(C#).
In other way I want to make my image like this http://myserver/image.jpg
so I can included in my JSON and download it in Android app.
As its not where clear from your question, This Link is the best what I can find for you. In it he is accessing ASP.NET WebAPI and converting all the things to JSON when access or pass it through the Andriod studio
http://hintdesk.com/how-to-call-asp-net-web-api-service-from-android/
and here is a complete series that can help you more
http://www.tutecentral.com/restful-api-for-android-part-1/
Hope this helps
I finally figure it out
This is the code
public HttpResponseMessage getImage(String name)
{
name = name + ".png";
var result = new HttpResponseMessage(HttpStatusCode.OK);
String filePath = HostingEnvironment.MapPath("~/images/"+name);
FileStream fileStream = new FileStream(filePath, FileMode.Open);
Image image = Image.FromStream(fileStream);
MemoryStream memoryStream = new MemoryStream();
image.Save(memoryStream, ImageFormat.Jpeg);
result.Content = new ByteArrayContent(memoryStream.ToArray());
result.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
fileStream.Dispose();
return result;
}
I need to upload a file using Stream (Azure Blobstorage), and just cannot find out how to get the stream from the object itself. See code below.
I'm new to the WebAPI and have used some examples. I'm getting the files and filedata, but it's not correct type for my methods to upload it. Therefore, I need to get or convert it into a normal Stream, which seems a bit hard at the moment :)
I know I need to use ReadAsStreamAsync().Result in some way, but it crashes in the foreach loop since I'm getting two provider.Contents (first one seems right, second one does not).
[System.Web.Http.HttpPost]
public async Task<HttpResponseMessage> Upload()
{
if (!Request.Content.IsMimeMultipartContent())
{
this.Request.CreateResponse(HttpStatusCode.UnsupportedMediaType);
}
var provider = GetMultipartProvider();
var result = await Request.Content.ReadAsMultipartAsync(provider);
// On upload, files are given a generic name like "BodyPart_26d6abe1-3ae1-416a-9429-b35f15e6e5d5"
// so this is how you can get the original file name
var originalFileName = GetDeserializedFileName(result.FileData.First());
// uploadedFileInfo object will give you some additional stuff like file length,
// creation time, directory name, a few filesystem methods etc..
var uploadedFileInfo = new FileInfo(result.FileData.First().LocalFileName);
// Remove this line as well as GetFormData method if you're not
// sending any form data with your upload request
var fileUploadObj = GetFormData<UploadDataModel>(result);
Stream filestream = null;
using (Stream stream = new MemoryStream())
{
foreach (HttpContent content in provider.Contents)
{
BinaryFormatter bFormatter = new BinaryFormatter();
bFormatter.Serialize(stream, content.ReadAsStreamAsync().Result);
stream.Position = 0;
filestream = stream;
}
}
var storage = new StorageServices();
storage.UploadBlob(filestream, originalFileName);**strong text**
private MultipartFormDataStreamProvider GetMultipartProvider()
{
var uploadFolder = "~/App_Data/Tmp/FileUploads"; // you could put this to web.config
var root = HttpContext.Current.Server.MapPath(uploadFolder);
Directory.CreateDirectory(root);
return new MultipartFormDataStreamProvider(root);
}
This is identical to a dilemma I had a few months ago (capturing the upload stream before the MultipartStreamProvider took over and auto-magically saved the stream to a file). The recommendation was to inherit that class and override the methods ... but that didn't work in my case. :( (I wanted the functionality of both the MultipartFileStreamProvider and MultipartFormDataStreamProvider rolled into one MultipartStreamProvider, without the autosave part).
This might help; here's one written by one of the Web API developers, and this from the same developer.
Hi just wanted to post my answer so if anybody encounters the same issue they can find a solution here itself.
here
MultipartMemoryStreamProvider stream = await this.Request.Content.ReadAsMultipartAsync();
foreach (var st in stream.Contents)
{
var fileBytes = await st.ReadAsByteArrayAsync();
string base64 = Convert.ToBase64String(fileBytes);
var contentHeader = st.Headers;
string filename = contentHeader.ContentDisposition.FileName.Replace("\"", "");
string filetype = contentHeader.ContentType.MediaType;
}
I used MultipartMemoryStreamProvider and got all the details like filename and filetype from the header of content.
Hope this helps someone.
I'm running the below code in my controller in an asp.net mvc project. I want to enable the user to view or download the files that I store on Cloud Files on rackspace.
var identity =
new CloudIdentity()
{
Username = "username",
APIKey = "apikey"
};
var storage = new CloudFilesProvider(identity);
Stream jpgStream = new MemoryStream();
storage.GetObject("files.container", "1.jpg", jpgStream);
Stream pdfStream = new MemoryStream();
storage.GetObject("files.container", "2.pdf", pdfStream);
var jpgResult = File(jpgStream, "Image/jpg", "1.jpg");
var pdfResult = File(pdfStream, "Application/pdf", "2.pdf");
The above code works when I return pdfResult. I get the correct file. But when I return the jpgResult, the browser downloads 1.jpg as an empty 0KB file.
Am I doing this the right way? Any idea what the problem might be?
Problem solved after I added:
jpgStream.Position = 0;
pdfStream.Position = 0;
Before the File() call. As per the question: File is empty and I don't understand why. Asp.net mvc FileResult
I don't know why this wasn't an issue with the pdf file.
You can also use the GetObjectSaveToFile method.