Open kos59125 opened 5 years ago
If you want to avoid putting large contents through remoting, I think your best bet is to make a non-Bolero endpoint and call it from Bolero using HttpClient
. I'll add documentation about it soon, but in short you can do something like this:
open System.Net.Http
let post (http: HttpClient, url: string, content: string) =
http.PostAsync(url, new StringContent(content))
|> Async.AwaitTask
let update (http: HttpClient) message model =
match message with
| ContentsRead contents ->
model, Cmd.ofAsync post (http, "/postFile", contents) (* ... *)
I'd actually be curious to see what performance difference you get between this and going through remoting. The only difference practically should be the JSON de/serialization.
Thanks. I'm considering over GB files to upload so that string
would not be appropriate.
Ah, this complicates things indeed. You probably want to use something like BlazorFileReader to stream the file contents from js, and post them using HttpClient
and StreamContent
.
Thanks again. I'll have a look at BlazorFileReader.
I am also trying to upload files. It's working in WebAssembly mode, but it does not work if AddBoleroHost
has server=true
or prerendered=true
. Let me explain why.
For the UI, instead of BlazorFileReader, I am using BlazorInputFile by Steve Sanderson. This works fine and can be styled with Bulma.
I've created a simple non-Bolero endpoint in MyApp.Server
by adding a MapPost
inside UseEndpoints
in my Startup.Configure
. This receives the uploaded files, which are sent from the client using MultipartFormDataContent
and HttpClient.PostAsync
.
I've used AddHttpClient
to create my HttpClient
so that I can initialize the BaseAddress
. I did this in Program.Main
in the client where I can access the base address from builder.HostEnvironment.BaseAddress
.
I'm actually calling AddHttpClient<IFileTransfer, FileTransfer>
to define a typed client, so the constructor of my FileTransfer
type receives the HttpClient
with the base address already set correctly. The MyApp
type in the client uses [<Inject>]
to obtain the IFileTransfer
service.
This all works fine.
The problem is that if I add services in Program.Main
in the client, it seems they are not available when the application is running in server mode (AddBoleroHost
has server=true
) or if the server needs to pre-render (AddBoleroHost
has prerender=true
). In these cases, accessing the IFileTransfer
service fails because it is not found.
Is this to be expected? Do services need to be added in two places?
Assuming so, I want to add it to Startup.ConfigureServices
in the server too. In this case, uploading a file will use an HttpClient
to upload to "localhost:5000". I can hardwire this, and it works for development, but how should I obtain the base address when using the HttpClient
to loop back to the Asp.Net Core server endpoint in this way?
Is this to be expected? Do services need to be added in two places?
Yes, that's how it goes: in client mode, the services are setup in Client's main function, and in server mode, in Startup's ConfigureServices.
Assuming so, I want to add it to Startup.ConfigureServices in the server too. In this case, uploading a file will use an HttpClient to upload to "localhost:5000". I can hardwire this, and it works for development, but how should I obtain the base address when using the HttpClient to loop back to the Asp.Net Core server endpoint in this way?
It seems like if you do things this way, then in server mode, you will have two requests: the first one, done by InputFile over SignalR, to send the data to the server-side Blazor; and then from the server to itself, to send it to the Post endpoint. It should be possible to avoid this second one.
If I understand correctly the purpose of your IFileTransfer
interface, you could provide two different implementations of it:
Thank you, that's a good solution. Two implementations of IFileTransfer
, one with an HttpClient
and one without.
In Program.Main
we have:
services.AddHttpClient<IFileTransfer, ClientFileTransfer> (fun http ->
http.BaseAddress <- Uri env.BaseAddress)
The implementation of IFileTransfer
posts to the endpoint in the server:
type ClientFileTransfer (http: HttpClient) =
interface IFileTransfer with
member _.UploadFiles files =
async {
// use MultipartFormDataContent and http.PostAsync
}
In Startup.ConfigureServices
we have:
AddSingleton<IFileTransfer, ServerFileTransfer>()
No uploading required. Just directly call the code that the upload endpoint uses.
type ServerFileTransfer () =
interface IFileTransfer with
member _.UploadFiles files = // share code with Http upload endpoint
I'd like to upload a file and process it on server-side.
I may do this to encode a file into string like base64, but can I upload a file not to decode on server-side?
The following codes illustrate what I'd like to do.
This works, but maybe not for large files.
This is what I'd like to do and doesn't work.
Do I need to prepare a non-Bolero endpoint and call it from JS directly?