Dart/Flutter web - Display uploaded image file - html

I'm choosing a file this way:
Future<String> getFile() {
final completer = new Completer<String>();
final InputElement input = document.createElement('input');
input
..type = 'file'
..accept = 'image/*';
input.onChange.listen((e) async {
final List<File> files = input.files;
final reader = new FileReader();
reader.readAsDataUrl(files[0]);
reader.onError.listen((error) => completer.completeError(error));
await reader.onLoad.first;
completer.complete(reader.result as String);
});
input.click();
return completer.future;
}
I would typically display the file using the widget returned by Image.file(...) but this accepts a dart:io File, not a dart:html one. What's the best way to display dart:html in Flutter web?
As a bonus question, are there any known browser limitations? I'm worried this solution will only work on Chrome.

Related

ASP.NET Core CSV text output formatter using CsvHelper

I'm looking to write custom ASP.NET Core TextOutputFormatter so I can throw any IEnumerable as an action result and it will produce comma separated list as an output. I don't want to save the results to a file or anything like it, just serve it as a response directly. CsvHelper seems to be very intuitive to use but I don't know how to write to response body. Any help will be much appreciated!
public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
{
var response = context.HttpContext.Response;
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
NewLine = Environment.NewLine,
Encoding = selectedEncoding,
};
if (context.Object is IEnumerable<object> list)
{
using var writer = new StreamWriter(response.Body);
using var csv = new CsvWriter(writer, config);
await csv.WriteRecordsAsync(list); // does not work
}
}
Edit:
When I replace
using var writer = new StreamWriter(response.Body);
with
StringBuilder sb = new();
using var writer = new StringWriter(sb);
I can see my sb is being built just as expected so it's not a problem with my IEnumerable<object> or with CsvWriter. I just don't know how to write results it produce to HttpContext.Response.Body
I'm not sure if this will be the best answer, but I was able to make this work using StringBuilder and replacing StreamWriter with StringWriter
public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding)
{
var response = context.HttpContext.Response;
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
NewLine = Environment.NewLine,
Encoding = selectedEncoding,
};
if (context.Object is IEnumerable<object> list)
{
StringBuilder sb = new();
using var writer = new StringWriter(sb);
using var csv = new CsvWriter(writer, config);
await csv.WriteRecordsAsync(list);
await response.WriteAsync(sb.ToString(), selectedEncoding);
}
}
WriteAsync requires adding Microsoft.AspNetCore.Http namespace

Why returning value from picked file is null ? Flutter

In my App I'm using image_picker and image_picker_web but it throws No podspec found for 'image_picker_web' in '.symlinks/plugins/image_picker_web/ios'exception when running on iOS.
So I decided not to use it and pick the file like in the accepted solution here How to Pick files and Images for upload with flutter web.
Print for picked file is correct but my method returns null a I guess it returns the variable before it gets assigned a value.
I don't know html so I'm kinda lost here..
What am I doing wrong with returning the picked value?
FileReader reader = FileReader();
InputElement uploadInput = FileUploadInputElement();
Future<Uint8List> pickImage() async {
print('FirebaseImageStorageRepositoryWeb. pickImage() started');
// image_picker_web works on web but creates pod problems on iOS
// Uint8List imageData;
// String fileName;
// await picker.getImage(source: ImageSource.gallery).then((picked) {
// picked.readAsBytes().then((data) {
// imageData = data;
// });
// });
// image_picker // notworking on web...
//html
Uint8List imageData;
InputElement uploadInput = FileUploadInputElement();
uploadInput.click();
uploadInput.onChange.listen((e) {
// read file content as dataURL
final files = uploadInput.files;
if (files.length == 1) {
final file = files[0];
print(
'selected file: type:${file.type},name: ${file.name}, size: ${file.size}');
reader.onLoadEnd.listen((e) {
imageData = reader.result;
// return imageData; // not working
});
reader.onError.listen((fileEvent) {
print('Some Error occured while reading the file');
});
reader.readAsArrayBuffer(file);
}
});
return imageData;
}
Following the answer at Image picker flutter web 1.9 (not the accepted one .. ) I could see what was wrong with the code above..
working:
#override
Future<Uint8List> pickImage() async {
print('FirebaseImageStorageRepositoryWeb.pickImage() started');
final completer = Completer<Uint8List>();
final InputElement input = document.createElement('input');
input
..type = 'file'
..multiple = true
..accept = 'image/*';
input.click();
// onChange doesn't work on mobile safari
input.addEventListener('change', (e) async {
final List<File> files = input.files;
Iterable<Future<Uint8List>> resultsFutures = files.map((file) {
final reader = FileReader();
reader.readAsArrayBuffer(files.first); // .readAsDataUrl(file);
reader.onError.listen((error) => completer.completeError(error));
return reader.onLoad.first.then((_) => reader.result);
});
final results = await Future.wait(resultsFutures);
completer.complete(results.first);
});
// need to append on mobile safari
document.body.append(input);
// input.click(); can be here
final Uint8List image = await completer.future;
input.remove();
return image;
}

Unity - Read JSON file From android [duplicate]

This is how i read my textfile in android.
#if UNITY_ANDROID
string full_path = string.Format("{0}/{1}",Application.streamingAssetsPath, path_with_extention_under_streaming_assets_folder);
// Android only use WWW to read file
WWW reader = new WWW(full_path);
while (!reader.isDone){}
json = reader.text;
// PK Debug 2017.12.11
Debug.Log(json);
#endif
and this is how i read my textfile from pc.
#if UNITY_STANDALONE
string full_path = string.Format("{0}/{1}", Application.streamingAssetsPath, path_with_extention_under_streaming_assets_folder);
StreamReader reader = new StreamReader(full_path);
json = reader.ReadToEnd().Trim();
reader.Close();
#endif
Now my problem is that i don't know how to write the file on mobile cause i do it like this on the standalone
#if UNITY_STANDALONE
StreamWriter writer = new StreamWriter(path, false);
writer.WriteLine(json);
writer.Close();
#endif
Help anyone
UPDATED QUESTION
This is the json file that it is in my streamingasset folder that i need to get
Now my problem is that i don't know how to write the file on mobile
cause I do it like this on the standalone
You can't save to this location. Application.streamingAssetsPath is read-only. It doesn't matter if it works on the Editor or not. It is read only and cannot be used to load data.
Reading data from the StreamingAssets:
IEnumerator loadStreamingAsset(string fileName)
{
string filePath = System.IO.Path.Combine(Application.streamingAssetsPath, fileName);
string result;
if (filePath.Contains("://") || filePath.Contains(":///"))
{
WWW www = new WWW(filePath);
yield return www;
result = www.text;
}
else
{
result = System.IO.File.ReadAllText(filePath);
}
Debug.Log("Loaded file: " + result);
}
Usage:
Let's load your "datacenter.json" file from your screenshot:
void Start()
{
StartCoroutine(loadStreamingAsset("datacenter.json"));
}
Saving Data:
The path to save a data that works on all platform is Application.persistentDataPath. Make sure to create a folder inside that path before saving data to it. The StreamReader in your question can be used to read or write to this path.
Saving to the Application.persistentDataPath path:
Use File.WriteAllBytes
Reading from the Application.persistentDataPath path
Use File.ReadAllBytes.
See this post for a complete example of how to save data in Unity.
This is the way I do it without the WWW class (works for Android an iOS), hope its useful
public void WriteDataToFile(string jsonString)
{
if (!Directory.Exists(folderPath))
{
Directory.CreateDirectory(folderPath);
}
if (!File.Exists(filePath))
{
File.Create(filePath).Close();
File.WriteAllText(filePath, jsonString);
}
else
{
File.WriteAllText(filePath, jsonString);
}
}

Angular - upload file as base64

I am trying to upload files from Angular 4 app to a JSON API service that accepts base64 strings as file content.
So what I do is - read the file with FileReader.readAsDataURL, then when user confirms the upload I will create a JSON request to the API and send the base64 string of the file I got earlier.
This is where the problem starts - as soon as I do something with the "content" (log it, send it, w/e) the request will be send, but its insanely slow, e.g. 20 seconds for 2MB file.
I have tried:
using ArrayBuffer and manually converting it to base64
storing the base64 string in HTML and retrieving it later
reading the files after user clicks on upload button
using the old client from #angular/common
using plain XHR request
but everything leads to the same result.
I know where the problem lies. But why does it happen? Is it something browser specific or angular specific? Is there a more preferred approach (keep in mind it has to be base64 string)?
Notes:
changing anything in the API is beyond my control
API is fine, sending any file trough postman will finish immediately
Code:
This method runs when user adds file to the dropzone:
public onFileChange(files: File[]) : void {
files.forEach((file: File, index: number) => {
const reader = new FileReader;
// UploadedFile is just a simple model that contains filename, size, type and later base64 content
this.uploadedFiles[index] = new UploadedFile(file);
//region reader.onprogress
reader.onprogress = (event: ProgressEvent) => {
if (event.lengthComputable) {
this.uploadedFiles[index].updateProgress(
Math.round((event.loaded * 100) / event.total)
);
}
};
//endregion
//region reader.onloadend
reader.onloadend = (event: ProgressEvent) => {
const target: FileReader = <FileReader>event.target;
const content = target.result.split(',')[1];
this.uploadedFiles[index].contentLoaded(content);
};
//endregion
reader.readAsDataURL(file);
});
}
This method runs when users clicks save button
public upload(uploadedFiles: UploadedFile[]) : Observable<null> {
const body: object = {
files: uploadedFiles.map((uploadedFile) => {
return {
filename: uploadedFile.name,
// SLOWDOWN HAPPENS HERE
content: uploadedFile.content
};
})
};
return this.http.post('file', body)
}
For sending big files to server you should use FormData to be able to send it as multi-part instead of a single big file.
Something like:
// import {Http, RequestOptions} from '#angular/http';
uploadFileToUrl(files, uploadUrl): Promise<any> {
// Note that setting a content-type header
// for mutlipart forms breaks some built in
// request parsers like multer in express.
const options = new RequestOptions();
const formData = new FormData();
// Append files to the virtual form.
for (const file of files) {
formData.append(file.name, file)
}
// Send it.
return this.http.post(uploadUrl, formData, options);
}
Also don't forget to set the header 'Content-Type': undefined, I've scratched my head over this for hours.

How Upload Files on Server using WebClient Windows phone?

I want upload a file (any type) on a server.
I have my file which is saved like this (I use FileAssociation)
await SharedStorageAccessManager.CopySharedFileAsync(ApplicationData.Current.LocalFolder, "fileToSave" + fileext, NameCollisionOption.ReplaceExisting, NavigationContext.QueryString["filetoken"]);
Then I get the saved file
StorageFolder folder = ApplicationData.Current.LocalFolder;
var file = await folder.GetFileAsync("fileToSave" + fileext);
Stream data = Application.GetResourceStream(new Uri(file.Path, UriKind.Relative)).Stream;
string filename = System.IO.Path.GetFileName(file.Path);
ServerFunctions.UploadFile(filename,data);
Then I start the Upload
internal void UploadFile(string fileName,Stream data)
{
WebClient web = new WebClient();
if (!string.IsNullOrEmpty(dataRequestParam.AuthentificationLogin))
{
System.Net.NetworkCredential account = new NetworkCredential(dataRequestParam.AuthentificationLogin, dataRequestParam.AuthentificationPassword);
web.Credentials = account;
}
web.AllowReadStreamBuffering = true;
web.AllowWriteStreamBuffering = true;
web.OpenWriteCompleted += (sender, e) =>
{
PushData(data, e.Result);
e.Result.Close();
data.Close();
};
web.OpenWriteAsync(dataRequestParam.TargetUri,"POST");
}
private void PushData(Stream input, Stream output)
{
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = input.Read(buffer, 0, buffer.Length)) != 0)
{
output.Write(buffer, 0, bytesRead);
}
}
The web server is supposed to send me as a response a xml with an error code or succes code inside.
None error is thrown but it doesnt work.And I don't understand why the e.result is a stream object. As I said the server should return a string...(xml file)
Could you bring me some explannations of what is happening in my code and if it will work with all types of files ?
Thanks
I think part of the problem here is that you're attempting to get this to behave like a streaming protocol when it seems you intend a request/response type architecture. For those purposes, you should consider working with a WebRequest object.
Bear with me as I fully qualify the namespace of the objects used inline, so it may get a little verbose, but I want you to know where to find these things.
internal async void UploadFile(string fileName, System.IO.Stream data)
{
// Specify URI, method, and credentials for the request
System.Net.WebRequest web = System.Net.HttpWebRequest.CreateHttp(dataRequestParam.TargetUri);
web.Method = "POST";
if (!string.IsNullOrEmpty(dataRequestParam.AuthenticationLogin))
{
web.Credentials = new System.Net.NetworkCredential(dataRequestParam.AuthenticationLogin, dataRequestParam.AuthenticationPassword);
}
// Create the request payload from the provided stream
System.IO.Stream requestStream =
await System.Threading.Tasks.Task<System.IO.Stream>.Factory.FromAsync(web.BeginGetRequestStream, web.EndGetRequestStream, null);
await data.CopyToAsync(requestStream);
// Get a response from the server
System.Net.WebResponse response =
await System.Threading.Tasks.Task<System.Net.WebResponse>.Factory.FromAsync(web.BeginGetResponse, web.EndGetResponse, null);
// Possibly parse the response with an XmlReader (example only)
System.Xml.XmlReader reader = System.Xml.XmlReader.Create(response.GetResponseStream());
string responseText = reader.ReadInnerXml(); // TODO: Real work here
}
The one oddity here is using the Task factory to create a task from the begin and end methods from getting both the request stream and the response. This makes it much simpler to consume these methods as you get a Task back which can be awaited for its return object, which you can then manipulate directly.
I'm not sure what form your response from the server takes on success versus failure, so I've simply shown how to create an XML reader to parse XML from the resulting stream. You can do whatever parsing is necessary yourself on these lines, but this should at least give you a look at what your server is returning in response.
The final code I use.
WebRequest web = HttpWebRequest.CreateHttp(dataRequestParam.TargetUri);
web.ContentType = dataRequestParam.ContentType;
web.Method = "POST";
web.ContentLength = data.Length;
if (!string.IsNullOrEmpty(dataRequestParam.AuthentificationLogin))
{
web.Credentials = new NetworkCredential(dataRequestParam.AuthentificationLogin, dataRequestParam.AuthentificationPassword);
}
using (var requestStream = await Task<Stream>.Factory.FromAsync(web.BeginGetRequestStream, web.EndGetRequestStream, web))
{
await data.CopyToAsync(requestStream);
}
WebResponse responseObject = await Task<WebResponse>.Factory.FromAsync(web.BeginGetResponse, web.EndGetResponse, web);
var responseStream = responseObject.GetResponseStream();
var sr = new StreamReader(responseStream);
string received = await sr.ReadToEndAsync();
return received;
}