I am making a flutter app and using the VideoPlayerController library package and requesting video content via network:
VideoPlayerController newController = VideoPlayerController.network(
"http://192.168.1.1:9999/S3/get-object/name-of-video.mp4");
My Web API Backend is .NET Core 3 and the controller endpoint is this:
[AllowAnonymous]
[HttpGet("get-object/{url}")]
public async Task<FileStreamResult> GetObject(string url)
{
// Seperate out only the filename
string[] res = url.Split(new string[] { "%2F" }, StringSplitOptions.None);
string fileName = res.LastOrDefault();
Stream imageStream = await S3Helper.ReadObjectData(_appSettings, fileName);
Response.Headers.Add("Content-Disposition", new ContentDisposition
{
FileName = fileName,
Inline = true // false = prompt the user for downloading; true = browser to try to show the file inline
}.ToString());
if (fileName.Contains(".jpg") || fileName.Contains(".jpeg"))
{
return File(imageStream, "image/jpeg");
}
else if (fileName.Contains(".png"))
{
return File(imageStream, "image/png");
}
else if (fileName.Contains(".mp4"))
{
return File(imageStream, new MediaTypeHeaderValue("video/mp4").MediaType, true);
}
else
{
return null;
}
}
However, when I create a widget that uses a Network image, it actually works. I'm not sure what the difference is.
CachedNetworkImage(
imageUrl: "http://192.168.1.1:9999/S3/get-object/name-of-image.jpg",
placeholder: (context, url) =>
CircularProgressIndicator(),
errorWidget: (context, url, error) =>
Icon(Icons.error),
fit: BoxFit.contain,
),
The .Net Core Backend has the video coming via an http get request as an inline video, similar to this one:
https://flutter.github.io/assets-for-api-docs/assets/videos/butterfly.mp4
The error I get from my flutter app shows up like this:
Source error. E/ExoPlayerImplInternal(24687): com.google.android.exoplayer2.upstream.HttpDataSource$HttpDataSourceException: Unable to connect to http://192.168.1.1:9999/S3/get-object/name-of-video.mp4
I don't know about ios. But android doesn't allow http://. You need to provide a link starting https://.
To allow the http:// or others
add this line on androids AndroidManifest.xml.
Location android\app\src\main\AndroidManifest.xml
android:usesCleartextTraffic="true"
This should look like this:
Related
We have two applications: A C# REST-API, and a Kotlin Android application, we are using Google Platform Cloud Bucket to host the images.
A picture will be uploaded on the Android application, but the C# REST-API needs to upload it to the Google Cloud Platform.
This is the working C# code to upload a file to the Google Cloud Buckets:
[HttpPost]
[Route("upload")]
public IActionResult Upload()
{
var storageClient = StorageClient.Create(google_credentials);
string fileToUpload ="/Users/niel/Downloads/new_cat.jpg";
using (var fileStream = new FileStream(fileToUpload, FileMode.Open,
FileAccess.Read, FileShare.Read))
{
storageClient.UploadObject("test_storage_fotos", "new_cat", "image/jpeg", fileStream);
}
Console.WriteLine("uploaded the file successfully");
return Ok();
}
Now I need to replace fileToUpload with the content from a POST-request. Is there a way to do this? Picture from Android app > C# API > Google Buckets? The link from the C# API to Google Buckets is already working.
Is there a way in Kotlin to somehow get the byte-string of an image, post it to my C# API who takes the content and puts it in a FileStream? I than can upload the FileStream using storageClient.UploadObject? Is this a possibility?
Thanks!
Yes, you can definitely do this. Just send the file over to the server via http protocol with multipart/form-data content type.
In kotlin you can use ktor or any other http library to do that.
For ktor you'll need to add an implementation dependency
implementation "io.ktor:ktor-client-android:1.5.4"
And you might also need to add additional permission in AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
Then you can send a file with this snippet. Notice that imageUri is a content uri, for file uri the code would be a bit different
private fun getFileName(resolver: ContentResolver, uri: Uri): String {
val returnCursor: Cursor = resolver.query(uri, null, null, null, null)!!
val nameIndex: Int = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
returnCursor.moveToFirst()
val name: String = returnCursor.getString(nameIndex)
returnCursor.close()
return name
}
suspend fun postAndImage(imageUri: Uri, uploadEndPoint: String) {
val client = HttpClient(Android)
val cr = applicationContext.contentResolver
if(cr.getType(imageUri) == null) {
//process error
return
}
val stream = cr.openInputStream(imageUri)
if(stream == null) {
//process error
return
}
val response: HttpResponse = client.submitFormWithBinaryData(
url = uploadEndPoint,
formData = formData {
append("image", InputProvider { stream.asInput() }, Headers.build {
append(HttpHeaders.ContentType, cr.getType(imageUri)!!)
append(HttpHeaders.ContentDisposition, "filename=${getFileName(cr, imageUri)}")
})
}
)
stream.close()
//process response
}
And you'll need to modify you upload function slightly
[HttpPost]
[Route("upload")]
//the name of the argument must match the key that you pass in "append" function
public async Task<IActionResult> Post(IFormFile image)
{
var storageClient = StorageClient.Create(google_credentials);
using (var stream = image.OpenReadStream())
{
//it's also possible to get original file name from file name property
var fileName = Guid.NewGuid() + "." + Path.GetExtension(image.FileName);
//assuming bucket is already created
var storageObject = await storageClient
.UploadObjectAsync("test_storage_fotos", fileName, "image/jpeg", stream);
//save information about a storage object in database
}
return Ok();
}
I created an ASP.NET Core api controller which return a FileStreamResult object. (I can change the type of result if needed)
Here is the code of the Get function:
[HttpGet("[action]/{p_gInspectionID}/{p_nIndex}")]
public async Task<FileStreamResult> GetInspectionPictureToDownload(Guid p_gInspectionID, int p_nIndex)
{
var l_strFilePath = await GetPictureFilePathAsync(p_gInspectionID, p_nIndex);
using (var l_sReader = System.IO.File.OpenRead(l_strFilePath))
{
return (File(l_sReader, "image/jpeg"));
}
}
Now I need to consume this result in the Blazor (Webassembly) client side application.
My goal is to have a button to launch the download of the file in the browser when user clicks on it.
This should launch download functionnality of the browser.
Is it possible to achieve this in Blazor client application ?
I was trying to do the same thing, but my API was authorized, so after reading this article I end up downloading the bytes in the web assembly application and use JavaScript to download the file from the bytes.
function downloadFromByteArray(options: {
byteArray: string,
fileName: string,
contentType: string
}): void {
// Convert base64 string to numbers array.
const numArray = atob(options.byteArray).split('').map(c => c.charCodeAt(0));
// Convert numbers array to Uint8Array object.
const uint8Array = new Uint8Array(numArray);
// Wrap it by Blob object.
const blob = new Blob([uint8Array], { type: options.contentType });
// Create "object URL" that is linked to the Blob object.
const url = URL.createObjectURL(blob);
// Invoke download helper function that implemented in
// the earlier section of this article.
downloadFromUrl({ url: url, fileName: options.fileName });
// At last, release unused resources.
URL.revokeObjectURL(url);
}
here is how I solved the problem. In fact the solution was really straightforward. Thank you #Data Juggler for pointing me in the right direction.
My Blazor solution holds two project:
the server side API (Blazor server)
the client side (Blazor WebAssembly).
Here is the code for the server side:
[AllowAnonymous]
[HttpGet("[action]/{p_strPictureFilePath}")]
public IActionResult GetInspectionPicture(string p_strPictureFilePath)
{
var l_sReader = System.IO.File.OpenRead(p_strPictureFilePath);
return (File(l_sReader, "application/octet-stream", Path.GetFileName(p_strPictureFilePath)));
}
... and the code on the client side:
Added this script in client-shared.js file:
window.downloadInspectionPicture = function (p_strServerFilePath)
{
var link = document.createElement('a');
link.href = 'api/Data/GetInspectionPicture/' + this.encodeURIComponent(p_strServerFilePath);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
of course, a reference to that file is present in index.html:
<script src="client-shared.js"></script>
And finally, added a link in the razor file, and invoke script when link is clicked:
Download
#code
{
[Inject]
IJSRuntime ThisJSRuntime { get; set; }
private async Task DownloadPictureAsync()
{
await ThisJSRuntime.InvokeVoidAsync("downloadInspectionPicture", ServerFilePath);
}
}
Hope my answer is clear and can be useful to someone
I don't know if in fact your way is possible, but what I do is similar for my site https://pixeldatabase.net .
The user clicks the download button, and I show a link like this:
public async void Download()
{
// Set the ImagePath
DownloadLink = ImagePath;
}
Then on the page, I just show a Download link conditionallay:
#if (HasDownloadLink)
{
<a class="downloadlink" download="#FileName" href="#DownloadLink"
target="_blank">Download</a>
}
Previously I was sending file as Byte array from ASP.net core 2.0 and in Angular 4 application I am calling below function to download the file
function (response) { // Here response is byte array
var url= window.URL.createObjectURL(res);
var link = document.createElement("a");
link.setAttribute("href", url);
link.setAttribute("download", this.zipLocation + ".zip");
link.style.display = "none";
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
But now I want to send the file path from the server like below
https://websiteaddress/file/path/to/download.ext
So in Angular 5, I can directly attach link to href attribute of anchor tag and will make automatic click on that. So I don't need to Convert byte array to url
Here the issue is I don't know how to create that downloadable file path using ASP.net core and send it to frontend
And also I want to know, which approach is better, whether sending Byte array or Sending the direct link? Is there any performance issue with any of the two?
If you are using api response as file data
add responseType: 'arraybuffer' in request header.
Try something like this:
HTML:
<a (click)="downLoad()">Click To Download</a>
TS:
downLoad(){
this.fileService.getFileFromServer(fileId.toString()).subscribe(respData => {
this.downLoadFile(respData, this.type);
}, error => {
});
}
/**
* Method is use to download file.
* #param data - Array Buffer data
* #param type - type of the document.
*/
downLoadFile(data: any, type: string) {
var blob = new Blob([data], { type: type.toString() });
var url = window.URL.createObjectURL(blob);
var pwa = window.open(url);
if (!pwa || pwa.closed || typeof pwa.closed == 'undefined') {
console.log('Please disable your Pop-up blocker and try again');
}
}
file-service.ts:
getFileFromServer(id){
return this.http.get(url, {responseType: 'arraybuffer',headers:headers});
}
your question make confuse about angular frontend and backend
frontend you can use mvc
<a asp-controller="Controller"
asp-action="Download"
asp-route-id="#Model.FileName">Download #Model.FileName</a>
or using angular
Download
<a [href]="ControllerRoute+'/Download?name='+fileName" download>Download {{fileName}}</a>
Ok maybe your problem is your action (in controller) doesnt server a file
you need return a HttpResponse with a MediaType, this is just a example, dont forget best practices on your code
[HttpGet]
public HttpResponseMessage GetDownloadableFIle(string name)
{
try
{
var result = new HttpResponseMessage(HttpStatusCode.OK);
var filePath = $"{MyRootPath}/{name}";
var bytes = File.ReadAllBytes(filePath );
result.Content = new ByteArrayContent(bytes);
var mediaType = "application/octet-stream";
result.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(mediaType);
return result;
}
catch (Exception ex)
{
throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.InternalServerError, ex.ToString()));
}
}
I'm working in a function to download pdfs from DropBox, I'm using ASP.net Core , everything works good. The only thing is that when you click in the download link it doesn't show any message and downloads the file. I would like to show the download progress like usually happens when we download something from Internet, I don't want any dialog to appear, just to show that the file was downloaded like normally happens in any browser like Chrome or IE and then have the choices 'Show in Folder' and things like that, what would I need to add?
public async Task DownloadPdf()
{
DropboxClient client2 = new DropboxClient("cU5M-a4exaAAAAAAAAABDVZsKdpPteNmwHslOeFEo-HByuOr4v4ONvXoAMCFyOXH");
string folder = "MyFolder";
string file = "Test PDF.pdf";
using (var response = await client2.Files.DownloadAsync("/" + folder + "/" + file))
{
using (var fileStream = System.IO.File.Create(#"C:\Users\User\Downloads\Test.pdf"))
{
(await response.GetContentAsStreamAsync()).CopyTo(fileStream);
}
}
}
I have a asp.net core project with an API that returns a file:
[HttpGet("{id}")]
public IActionResult Get(int id) {
byte[] fileContent = READ_YOUR_FILE();
FileContentResult result = new FileContentResult(fileContent, "application/octet-stream") {
FileDownloadName = id.ToString()
};
return result;
}
If I access in my browser the URL from this API (myapp/api/mycontroller/id), then I can see the file downloading.
I have PDF file placed on different (FILE-Server) server machine, and the IIS machine on which my MVC application is hosted have rights to that File-Server. From IIS machine i can access the file through following URI:
file://file-server/data-folder/pdf/19450205.pdf
I want to enable my MVC app's users to download their respective files by clicking on download link or button. So probably i would have to write some Action for that link/button.
I tried to use File return type for my Action method in following way:
public ActionResult FileDownload()
{
string filePth = #"file://file-server/data-folder/pdf/19450205.pdf";
return File(filePth , "application/pdf");
}
but the above code gives exception of URI not supported.
I also tried to use FileStream to read bytes inside array return that bytes towards download, but FileStream also gives error of not proper "Virtual Path" as the file is not placed inside virtual path, its on separate server.
public ActionResult Download()
{
var document = = #"file://file-server/data-folder/pdf/19450205.pdf";
var cd = new System.Net.Mime.ContentDisposition
{
// for example foo.bak
FileName = document.FileName,
// always prompt the user for downloading, set to true if you want
// the browser to try to show the file inline
Inline = false,
};
Response.AppendHeader("Content-Disposition", cd.ToString());
return File(document.Data, document.ContentType);
}
Thanks for the replies, but both suggestion did not work.
as file needs to be accessed over URI, using FileInfo gives error: URI formats are not supported.
I managed to get this done through following mechanism:
public ActionResult FaxFileDownload()
{
string filePth = #"file://file-server/data-folder/pdf/19450205.pdf";
WebClient wc = new WebClient();
Stream s = wc.OpenRead(filePth);
return File(s, "application/pdf");
}
Thanks to All.