I have problem with streaming video file in my controller to eg. VLC or HTML5 video
My code
public IActionResult VideoStreamContent()
{
var path = #"C:\video1.mp4";
var stream = System.IO.File.OpenRead(path);
return new FileStreamResult(stream, "application/octet-stream");
}
Browser can download that file from this controller but when i want make it in VLC then VLC cannot open that stream
You should try getting the proper mime type for your file before sending it down. That way clients can know how to handle the file type.
public IActionResult VideoStreamContent() {
var path = #"C:\video1.mp4";
var stream = System.IO.File.OpenRead(path);
var mimeType = GetMimeType(path);
return new FileStreamResult(stream, mimeType??"application/octet-stream");
}
string GetMimeType(string fileName) {
//Insert code here to get mime type of file
}
There are many questions/answers on SO about how to get the mime types if you look for it.
Related
I'm converting ASP.NET WebForms code to ASP.NET Core Razor pages which is new to me. I'm trying to retrieve an image MemoryStream from a business class (based on SixLabors awesome ImageSharp) and have the page render the JPEG -- no HTML, just the image. I intend to use this page elsewhere as an <img> src, like <img src="Render?imageID=42&mode=invert" />
In Render.cshtml.cs:
public class RenderModel : PageModel
{
public void OnGet()
{
//snip
Stream stream = new MemoryStream();
using (Image image1 = Image.Load(imagePath))
{
SixLabors.ImageSharp.Formats.Jpeg.JpegEncoder encoder = new SixLabors.ImageSharp.Formats.Jpeg.JpegEncoder();
encoder.Quality = 75;
image1.Save(stream, encoder);
//image.Save("/temp/xxx.jpg", encoder); //test to see image. it works
}
Response.Clear();
//Response.Headers.ContentLength = stream.Length;
Response.ContentType = "image/jpeg";
Response.Body = stream;
}
}
...but this is not working, I get:
System.InvalidOperationException: Response Content-Length mismatch: too few bytes written (0 of 135408).
135408 is the stream.Length.
I'm probably not doing this correctly in the ASP.NET Core/Razor way. Can anyone set me straight as to how to do this? Thanks!
EDIT: commenting out the Headers.ContentLength fixes the error. But now I get a broken-image icon in the browser. Closer...
You need to write to the Response.Body isntead of replacing it.
stream.Seek(0, SeekOrigin.Begin);
await stream.CopyToAsync(Response.Body);
await Response.Body.FlushAsync();
I think Razor pages are intented to return html content.
However it seems to be possible to return different types of result in OnGet e.g. you could return a new FileContentReset (FileStreamResult seems to have issues with contentlength)
// read as bytes
public FileContentResult OnGet()
{
var image = System.IO.File.ReadAllBytes(#"c:\temp\myimage.jpeg");
return new FileContentResult(image.ToArray(), "image/jpeg");
}
// example if image comes from stream
public FileContentResult OnGet()
{
using var imageStr = System.IO.File.Open(#"c:\temp\myimage.jpeg", FileMode.Open);
using var memStr = new MemoryStream();
imageStr.CopyTo(memStr);
return new FileContentResult(memStr.ToArray(), "image/jpeg");
}
Even better maybe it to not use a Razor page and to add a MVC controller to return the result.
In the wwwroot folder of my .Net Core MVC application, I have some images. I need to serve these pictures to TopDesk, where I can put in a url which gets embedded. I have no influence on Topdesk. I can only change the way the image is served.
When I use a direct link to the image, it works. The image gets embedded
Example of direct url:
https://my.web.site/images/image001.jpeg
But there is a limited embedded size (600px) so i need to resize the images. For that purpose I wrote a very simple api controller:
[HttpGet]
[Route("api/Images/GetImage/{id}")]
public IActionResult GetImage(string id)
{
try
{
var pad = $"c:\\Images\\{id}";
if(System.IO.File.Exists(path))
{
var fileBytes = System.IO.File.ReadAllBytes(path);
var smallImage = ..... doing resizing;
new FileExtensionContentTypeProvider().TryGetContentType(Path.GetFileName(path), out var contentType);
return File(smallImage , contentType ?? "application/octet-stream", $"{id}");
}
return NotFound();
}
catch(Exception ex)
{
return BadRequest(ex.Message);
}
}
but the url
https://my.web.site/api/images/GetImage/image001.jpeg
results in
Resource interpreted as Document but transferred with MIME type
image/jpeg
The image doesn't show.
When i test the url in Postman, it returns the image without warning.
What am i missing here?
Instead of returning a File, try using FileContentResult instead:
[HttpGet]
[Route("api/Images/GetImage/{id}")]
public IActionResult GetImage(string id)
{
try
{
var path = $"c:\\Images\\{id}";
if(System.IO.File.Exists(path))
{
var fileBytes = System.IO.File.ReadAllBytes(path);
var smallImage = ..... doing resizing;
new FileExtensionContentTypeProvider().TryGetContentType(Path.GetFileName(path), out var contentType);
return new FileContentResult(fileBytes, contentType ?? "application/octet-stream");
}
return NotFound();
}
catch(Exception ex)
{
return BadRequest(ex.Message);
}
}
When navigating to /GetImage/{id} with a browser, you will see that with File the browser tends to download the file, but with FileContentResult it displays the image in the browser tab directly, which is the same behavior as using static files. This is probably happening because of the Response Headers being added when using File/FileContentResult (probably a Content-Disposition header). Not sure how TopDesk is using these images though.
Off-topic: It's also a good practice to not instantiate a FileExtensionContentTypeProvider with every request. Instead, you can register it as a singleton in your Startup.cs like:
services.AddSingleton(new FileExtensionContentTypeProvider());
and inject it in your controller's constructor.
I am trying to upload a file onto my Drive using Google Drive .NET API v3. My code is below
static string[] Scopes = { DriveService.Scope.Drive,
DriveService.Scope.DriveAppdata,
DriveService.Scope.DriveFile,
DriveService.Scope.DriveMetadataReadonly,
DriveService.Scope.DriveReadonly,
DriveService.Scope.DriveScripts };
static string ApplicationName = "Drive API .NET Quickstart";
public ActionResult Index()
{
UserCredential credential;
using (var stream =
new FileStream("C:/Users/admin1/Documents/visual studio 2017/Projects/TryGoogleDrive/TryGoogleDrive/client_secret.json", FileMode.Open, FileAccess.Read))
{
string credPath = Environment.GetFolderPath(
System.Environment.SpecialFolder.Personal);
credPath = Path.Combine(credPath, ".credentials/drive-dotnet-quickstart.json");
credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.Load(stream).Secrets,
Scopes,
"user",
CancellationToken.None,
new FileDataStore(credPath, true)).Result;
Debug.WriteLine("Credential file saved to: " + credPath);
}
// Create Drive API service.
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = ApplicationName,
});
// Define parameters of request.
FilesResource.ListRequest listRequest = service.Files.List();
listRequest.PageSize = 10;
listRequest.Fields = "nextPageToken, files(id, name)";
// List files.
IList<Google.Apis.Drive.v3.Data.File> files = listRequest.Execute()
.Files;
Debug.WriteLine("Files:");
if (files != null && files.Count > 0)
{
foreach (var file in files)
{
Debug.WriteLine("{0} ({1})", file.Name, file.Id);
}
}
else
{
Debug.WriteLine("No files found.");
}
var fileMetadata = new Google.Apis.Drive.v3.Data.File()
{
Name = "report.csv",
MimeType = "text/csv",
};
FilesResource.CreateMediaUpload request;
using (var stream = new FileStream("C:/debugging/report.csv",
FileMode.Open))
{
request = service.Files.Create(
fileMetadata, stream, "text/csv");
request.Fields = "id";
request.Upload();
}
var response = request.ResponseBody;
Console.WriteLine("File ID: " + response.Id);
return View();
}
The problem I'm facing is that response is always null. I looked into it a bit further and found that the request returned a 403 resultCode. I also took a look at some other questions on SO this and this but neither were of any help.
Edit: I forgot to mention that the first part of the code is working correctly - it lists all the files in my Drive. Only the second part is not working (the upload file part)
string[] Scopes = { DriveService.Scope.Drive };
Change the Drive scope then delete the file token.json
in vs2017 you can see token.json file in token.json folder when client_secret.json file present.
Try to visit this post from ASP.NET forum.
The same idea as what you want to do in your app, since you are dealing with uploading a file in Google Drive using .net.
You may try to call rest api directly to achieve your requirement :
The quickstart from .net will help you to make requests from/to the Drive API.
Upload Files:
The Drive API allows you to upload file data when create or
updating a File resource.
You can send upload requests in any of the following ways:
Simple upload: uploadType=media. For quick transfer of a small file (5 MB or less). To perform a simple upload, refer to Performing
a Simple Upload.
Multipart upload: uploadType=multipart. For quick transfer of a small file (5 MB or less) and metadata describing the file, all in a
single request. To perform a multipart upload, refer to Performing a
Multipart Upload.
Resumable upload: uploadType=resumable. For more reliable transfer, especially important with large files. Resumable uploads are
a good choice for most applications, since they also work for small
files at the cost of one additional HTTP request per upload. To
perform a resumable upload, refer to Performing a Resumable
Upload.
You may try this code from the documentation on uploading sample file.
var fileMetadata = new File()
{
Name = "photo.jpg"
};
FilesResource.CreateMediaUpload request;
using (var stream = new System.IO.FileStream("files/photo.jpg",
System.IO.FileMode.Open))
{
request = driveService.Files.Create(
fileMetadata, stream, "image/jpeg");
request.Fields = "id";
request.Upload();
}
var file = request.ResponseBody;
Console.WriteLine("File ID: " + file.Id);
You may check the errors you may encounter in this documentation.
Have a look at what request.Upload() returns. For me when I was having this issue it returned:
Insufficient Permission Errors [Message[Insufficient Permission] Location[ - ]
I changed my scope from DriveService.Scope.DriveReadonly to DriveService.Scope.Drive and I was in business.
Change static string[] Scopes = { DriveService.Scope.DriveReadonly }; to static string[] Scopes = { DriveService.Scope.Drive };.
After changes, take a look into token.json file and check does it change its scope from DriveReadonly to Drive.
If you are seeing DriveReadonly then delete the token.json file and run the application again.
My server has an API to upload files and convert them to PDF. Right now, the file gets uploaded, saved to disk and then converted. See the (trimmed down) code below:
public class ConversionController : ApiController {
public async Task<HttpResponseMessage> PostData() {
var root = HttpContext.Current.Server.MapPath("~/App_Data");
var provider = new MultipartFormDataStreamProvider(root);
await Request.Content.ReadAsMultipartAsync(provider);
var file = provider.FileData.First();
var originalName = file.Headers.ContentDisposition.FileName;
var fileStream = new FileStream(file.LocalFileName, FileMode.Open, FileAccess.Read);
// convert file stream and return the PDF response ...
}
}
As you can see, I read the file to disk but then immediately get a stream for it so I can feed it to our conversion function (which takes a stream). This seems like a waste to save the file to disk every time. So instead of ReadAsMultipartAsync() which saves to disk, I can use ReadAsStreamAsync() which will give me the stream that I can give directly to the conversion function.
The problem that I'm having with ReadAsMultipartAsync() is that I can't figure out how to get the original file name without having the MultipartFileData instance to work with. I know that the name comes with the request as part of the body, but I can't figure out how to access it. How can I get the name of the uploaded file without writing the uploaded file to disk?
You can use MultipartMemoryStreamProvider, for example:
var provider = new MultipartMemoryStreamProvider();
var task = Request.Content.ReadAsMultipartAsync(provider).ContinueWith(t =>
{
var file = provider.Contents.First();
var fileContents = await file.ReadAsByteArrayAsync();
var filename = file.Headers.ContentDisposition.FileName.Replace("\"", string.Empty);
/// do other stuff
return Request.CreateResponse(HttpStatusCode.OK);
});
In this case the content is read as a byte array, but the same applies to streams.
I'm trying to drag and drop file upload with a progress bar.
I have a div which is listening to files being dropped on which is working perfectly.
I'm then..
//Setting up a XmlHttpRequest
xhr = new XMLHttpRequest();
//Open connection
xhr.open("post", "api/ImageUpload", true);
// Set appropriate headers
xhr.setRequestHeader("Content-Type", "multipart/form-data");
xhr.setRequestHeader("X-File-Type", uf.type);
xhr.setRequestHeader("X-File-Name", uf.name);
xhr.setRequestHeader("X-File-Size", uf.size);
This sends fine, with the stream as the body of the request to the Web API (not async).
[System.Web.Mvc.HttpPost]
public string Post()
{
Stream stream = HttpContext.Current.Request.InputStream;
String filename = HttpContext.Current.Request.Headers["X-File-Name"];
FileModel file = uploadService.UploadFile(stream, filename);
return file.Id.ToString();
}
I'm trying to chance the request to "public async Task< string> Post(){ }
If the method was using a multipart form on the page instead of XmlHttpRequest I would have used "await Request.Content.ReadAsMultipartAsync(provider)" but this doesn't seem to be populated at the time I need it.
So what is the correct was to handle and an Async call from XmlHttpRequest on a Web API in order to record progress during the request with XHR's progress event?
I have looked at a great deal of pages so far to find a solution but this is the page I have used primarily.
http://robertnyman.com/html5/fileapi-upload/fileapi-upload.html
Thanks for any help
Oliver
It looks like someone else had the same question with you and got an answer yet. please have a look at ASP.NET MVC 4 Web Api ajax file upload.
And here is an example from microsoft http://www.asp.net/web-api/overview/working-with-http/sending-html-form-data,-part-2.
I combined the two above solution together and worked for me (just adjust a little bit)
one line change in Javascritp
xhr.open("post", "api/upload", true);
Save the file using stream
public class UploadController : ApiController
{
public async Task<HttpResponseMessage> PostFormData()
{
string root = HttpContext.Current.Server.MapPath("~/App_Data");
var fileName = Path.Combine(root, Request.Headers.GetValues("X-File-Name").First());
try
{
var writer = new StreamWriter(fileName);
await Request.Content.CopyToAsync(writer.BaseStream);
writer.Close();
return Request.CreateResponse(HttpStatusCode.OK);
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
}
}
}