How to create a CDN server in dotnet core using MongoDB GridFS and AngularJs - mongodb

We want to create a decoupled file server named CDN in .net core using MongoDB GridFS and angular js.
From various sources I tried my best to solve the issue.
And finally we done this.

I used Visual Studio 2017 and .NETCore 1.1
To do so I follow the followings:
1. Write Code in MongoDB GridFS
create an interface like
public interface IGridFsRepository : IDisposable
{
Task<string> UploadAsync(IFormFile file);
Task<bool> AnyAsync(ObjectId id);
Task<bool> AnyAsync(string fileName);
Task DeleteAsync(string fileName);
Task DeleteAsync(ObjectId id);
Task<GridFSDownloadStream<ObjectId>> DownloadAsync(string fileName);
Task<GridFSDownloadStream<ObjectId>> DownloadAsync(ObjectId id);
object GetAllFilesByContentType(string contentType, int skip, int
take);
object GetAllFiles(int skip, int take);
}
then create MongoDbCdnContext:
public abstract class MongoDbCdnContext
{
public IGridFSBucket GridFsBucket {get;}
protected MongoDbCdnContext(string connectionStringName)
{
var config = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json")
.Build();
var connectionString =
config.GetConnectionString(connectionStringName);
var connection = new MongoUrl(connectionString);
var settings = MongoClientSettings.FromUrl(connection);
//#if DEBUG
// settings.ClusterConfigurator = builder =>
builder.Subscribe<CommandStartedEvent>(started =>
// {
// Debug.Write(started.Command);
// });
//#endif
var client = new MongoClient(settings);
var database = client.GetDatabase(connection.DatabaseName);
GridFsBucket = new GridFSBucket(database);
}
}
then implement it:
public class GridFsRepository : MongoDbCdnContext,
IGridFsRepository
{
public GridFsRepository() : base("MongoCdn")
{
}
public async Task<string> UploadAsync(IFormFile file)
{
var options = new GridFSUploadOptions
{
Metadata = new BsonDocument("contentType", file.ContentType)
};
using (var reader = new
StreamReader((Stream)file.OpenReadStream()))
{
var stream = reader.BaseStream;
var fileId = await
GridFsBucket.UploadFromStreamAsync(file.FileName, stream,
options);
return fileId.ToString();
}
}
public async Task<bool> AnyAsync(ObjectId id)
{
var filter = Builders<GridFSFileInfo>.Filter.Eq("_id", id);
return await GridFsBucket.Find(filter).AnyAsync();
}
public Task<bool> AnyAsync(string fileName)
{
var filter = Builders<GridFSFileInfo>.Filter.Where(x =>
x.Filename == fileName);
return GridFsBucket.Find(filter).AnyAsync();
}
public async Task DeleteAsync(string fileName)
{
var fileInfo = await GetFileInfoAsync(fileName);
if (fileInfo != null)
await DeleteAsync(fileInfo.Id);
}
public async Task DeleteAsync(ObjectId id)
{
await GridFsBucket.DeleteAsync(id);
}
private async Task<GridFSFileInfo> GetFileInfoAsync(string fileName)
{
var filter = Builders<GridFSFileInfo>.Filter.Eq(x => x.Filename,
fileName);
var fileInfo = await
GridFsBucket.Find(filter).FirstOrDefaultAsync();
return fileInfo;
}
public async Task<GridFSDownloadStream<ObjectId>>
DownloadAsync(ObjectId id)
{
return await GridFsBucket.OpenDownloadStreamAsync(id);
}
public async Task<GridFSDownloadStream<ObjectId>>
DownloadAsync(string fileName)
{
return await
GridFsBucket.OpenDownloadStreamByNameAsync(fileName);
}
public IEnumerable<GridFSFileInfoDto> GetAllFilesByContentType(string
contentType, int skip, int take)
{
var filter = Builders<GridFSFileInfo>.Filter
.Eq(info => info.Metadata, new BsonDocument(new
BsonElement("contentType", contentType)));
var options = new GridFSFindOptions
{
Limit = take,
Skip = skip,
};
var stream = GridFsBucket.Find(filter, options)
.ToList()
.Select(s => new GridFSFileInfoDto
{
Id = s.Id,
Filename = s.Filename,
MetaData = s.Metadata,
Length = s.Length + "",
UploadDateTime = s.UploadDateTime,
})
.ToList();
return stream;
}
public IEnumerable<GridFSFileInfoDto> GetAllFiles(int skip, int take)
{
var options = new GridFSFindOptions
{
Limit = take,
Skip = skip,
};
var stream = GridFsBucket.Find(new
BsonDocumentFilterDefinition<GridFSFileInfo<ObjectId>>(new
BsonDocument()), options)
.ToList()
.Select(s => new GridFSFileInfoDto
{
Id = s.Id,
Filename = s.Filename,
MetaData = s.Metadata,
Length = s.Length + "",
UploadDateTime = s.UploadDateTime,
})
.ToList();
return stream;
}
public void Dispose()
{
GC.SuppressFinalize(this);
}
}
then create a controller in .netcore web api
[EnableCors("AllowAll")]
[ValidateModel]
[Route("api/files")]
public class FileController : Controller
{
private readonly IGridFsRepository _gridFsManager;
public FileController(IGridFsRepository gridFsRepository)
{
_gridFsManager = gridFsRepository;
}
[AllowAnonymous]
[HttpGet("{fileName}",Name = "GetByFileName")]
public async Task<IActionResult> GetByFileName(string fileName)
{
return Ok(await _gridFsManager.DownloadAsync(fileName));
}
[AllowAnonymous]
[HttpGet("{id}",Name = "GetById")]
public async Task<IActionResult> GetByFileName(ObjectId id)
{
return Ok(await _gridFsManager.DownloadAsync(id));
}
[HttpPost]
public async Task<IActionResult> Upload([FromForm] IFormFile file)
{
if (file != null)
{
if (file.ContentType.Contains("image"))
return BadRequest("Sorry only image jpg/jpeg/png
accepted");
if (file.Length >= (300 * 1024))
return BadRequest($"Sorry {file.FileName} is exceeds
300kb");
await _gridFsManager.DeleteAsync(file.FileName);
await _gridFsManager.UploadAsync(file);
}
return NoContent();
}
[HttpDelete]
public async Task<IActionResult> Delete(string id)
{
await _gridFsManager.DeleteAsync(id);
return NoContent();
}
}
please do not forget to resolve dependency:
services.AddScoped<IGridFsRepository, GridFsRepository>();
to file from html:
<div class="btn">
<span>Logo</span>
<input type="file" data-ng-model="cp.data.file"
id="selectedFile" name="selectedFile">
</div>
lets go to UI layer:
first create an angular factory:
(function () {
"use strict";
angular.module("appCdn", [])
.factory('fileUploader', ["$http", function ($http) {
return {
upload: function (url, file, fileMaxSize, fileName, callback) {
if (this.isValidFileSize(fileMaxSize, file)) {
var fd = new FormData(); //Create FormData object
if (fileName)
fd.append("file", file, fileName);
else
fd.append("file", file);
$http.post(url, fd, {
transformRequest: angular.identity,
headers: { 'Content-Type': undefined }
}).success(function (data) {
callback();
}).error(function (data) {
Materialize.toast("Sorry! error in file upload", 4000, 'red');
});
} else {
Materialize.toast("Sorry! " + file.name + " exceeds 300kb", 4000, 'red');
}
},
isValidFileSize: function (maximumAllowedSize, file) {
if (file.size >= maximumAllowedSize) {
return false;
} else {
return true;
}
}
};
}
]);
})();
after that create an angular controller:
angular.module('ecom').controller('companyProfileCtrl',
['$http', 'config', "confirmation", "fileUploader",companyProfile]);
function companyProfile($http, config, confirmation, fileUploader) {
var vm = this; vm.getProfile = function () {
$http.get(config.apiUrl + "companyProfile")
.success(function (response) {
vm.data = response;
});
};
vm.save = function (profile) {
confirmation.confirm(function () {
$http.post(config.apiUrl + "companyProfile", profile)
.success(function (data) {
var fileName = "";
if (profile.id) {
fileName = profile.id;
} else {
fileName = data;
}
vm.upload(fileName,
function () {
Materialize.toast("succeeded", 4000, 'green');
window.location.href = window.history.back();
});
})
.error(function (data) {
Materialize.toast(data, 4000, 'red');
});
});
};
vm.upload = function (fileName, callback) {
var photo = document.getElementById("selectedFile");
var file = photo.files[0];
if (file) {fileUploader.upload(config.cdnUrl + "files",
//300kb
file, 1024 * 300, fileName, callback);
}
};
};
finally to show the image in html:
<div><img src="http://localhost:41792/api/files/{{cp.data.id}}"
class="img-responsive visible-md visible-lg img-margin-desktop"
width="350" height="167" />
</div>
finally we done this. this is all.
I always looking forward to receiving criticism.

Related

Maui - FilePicker - Return TextFile FullPath Asynchronously

The goal is to have a Maui class library that will have a function that return a full path to read a text file in my Maui application.
Can you help me to fix the following code?
The error is when I try to return the FullPath as string
var FileFullPath = await result.FullPath.ToString();
Here the error description
Severity Code Description Project File Line Suppression State Error CS1061
'string' does not contain a definition for 'GetAwaiter' and no accessible extension method 'GetAwaiter' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) MPCFilePickerMauiLibrary (net7.0), MPCFilePickerMauiLibrary (net7.0-android), MPCFilePickerMauiLibrary (net7.0-ios), MPCFilePickerMauiLibrary (net7.0-maccatalyst) D:\repos\MPC-MassPropertiesCalculator\MPCFilePickerMauiLibrary\PickTxtFile.cs 35 Active
Here is the Maui class library that have created.
using Microsoft.Maui.Storage;
namespace MPCFilePickerMauiLibrary;
//Ref https://youtu.be/C6LV_xMGdKc - Intro To Class Libraries in C#
public class PickTxtFile
{
public static async Task<string> GetFilePathAsync()
{
//For custom file types
var customFileType = new FilePickerFileType(
new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.iOS, new[] { "public.text" } }, // UTType values
{ DevicePlatform.Android, new[] { "text/plain" } }, // MIME type
{ DevicePlatform.WinUI, new[] { ".Txt" } }, // file extension
{ DevicePlatform.Tizen, new[] { "*/*" } },
{ DevicePlatform.macOS, new[] { "Txt" } }, // UTType values
});
var result = await FilePicker.PickAsync(new PickOptions
{
PickerTitle = "Pick MPC Demo file Please",
FileTypes = customFileType
});
if (result == null)
return "";
var FileFullPath = await result.FullPath.ToString();
return FileFullPath;
}
Here is the code where I'm using the function
using MPCFilePickerMauiLibrary;
using Microsoft.UI.Xaml.Controls;
namespace MPC_MassPropertiesCalculator_MAUIapp.Views;
public partial class MPCFileDemo : ContentPage
{
public MPCFileDemo()
{
InitializeComponent();
}
private void MenuFlyoutItem_Clicked(object sender, EventArgs e)
{
String filePath = PickTxtFile.GetFilePathAsync();
if (File.Exists(filePath))
{
//TODO Read file
}
}
}
With Jason's comments, here is the solution.
Maui class library
using Microsoft.Maui.Storage;
namespace MPCFilePickerMauiLibrary;
//Ref https://youtu.be/C6LV_xMGdKc - Intro To Class Libraries in C#
public class PickTxtFile
{
public static async Task<string> GetFilePathAsync()
{
//For custom file types
var customFileType = new FilePickerFileType(
new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.iOS, new[] { "public.text" } }, // UTType values
{ DevicePlatform.Android, new[] { "text/plain" } }, // MIME type
{ DevicePlatform.WinUI, new[] { ".Txt" } }, // file extension
{ DevicePlatform.Tizen, new[] { "*/*" } },
{ DevicePlatform.macOS, new[] { "Txt" } }, // UTType values
});
var result = await FilePicker.PickAsync(new PickOptions
{
PickerTitle = "Pick MPC Demo file Please",
FileTypes = customFileType
});
if (result == null)
return "";
var FileFullPath = result.FullPath.ToString();
return FileFullPath;
}
}
Code in the application
using MPCFilePickerMauiLibrary;
namespace MPC_MassPropertiesCalculator_MAUIapp.Views;
public partial class MPCFileDemo : ContentPage
{
public MPCFileDemo()
{
InitializeComponent();
}
private async void MenuFlyoutItem_Clicked(object sender, EventArgs e)
{
string? filePath = await PickTxtFile.GetFilePathAsync();
if (File.Exists(filePath))
{
await DisplayAlert("File Path", $"File Path is: {filePath}", "OK");
}
else
{
await DisplayAlert("File Path", "Usre did not select a file", "OK");
}
}
}
Output result image.

I am using c# services and controllers with mongodb , facing issue in patch request I can patch using JsonPatchDocument but cannot save to mongo db

I am using MongoDB. I can post put and delete also get, can patch using JsonPatchDocument but face issue when trying to update in db.
Last Methods in UserService and UserController, which I want make for patch using JsonPatchDocument
This is my appsetting.json file
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"HealthCareDatabase": {
"ConnectionString": "mongodb://localhost:27017",
"DatabaseName": "HealthApp",
"UserCollectionName":"User",
"JobCollectionName":"Jobs",
"JobApplicationCollectionName":"jobApplications",
"EducationCollectionName":"Education",
"ExperienceCollectionName":"Experience",
"TestCollectionName":"Test"
}
}
UserServices.cs
using backend.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using MongoDB.Driver;
using Microsoft.AspNetCore.JsonPatch;
namespace backend.Services;
public class UserService :ControllerBase
{
private readonly IMongoCollection<User> _userCollection;
public UserService(
IOptions<HealthCareDbSettings> HealthCareDbSettings)
{
var mongoClient = new MongoClient(
HealthCareDbSettings.Value.ConnectionString);
var mongoDatabase = mongoClient.GetDatabase(
HealthCareDbSettings.Value.DatabaseName);
_userCollection = mongoDatabase.GetCollection<User>(
HealthCareDbSettings.Value.UserCollectionName);
}
// User Services are down there
public async Task<List<User>> GetAsync() =>
await _userCollection.Find(_ => true).ToListAsync();
public async Task<User?> GetAsync(string id) =>
await _userCollection.Find(user => user.Id == id).FirstOrDefaultAsync();
public User Login(string email, string password)
{
var user = _userCollection.Find(user => user.Email == email ).FirstOrDefault();
bool isPasswordValid = BCrypt.Net.BCrypt.Verify(password,user.Password);
if (isPasswordValid)
{
return user;
}
else{
return null;
}
}
public async Task CreateUser(User newUser) =>
await _userCollection.InsertOneAsync(newUser);
public async Task UpdateUser(string id,User user) =>
await _userCollection.ReplaceOneAsync(user => user.Id == id,user);
// This method has error
public async Task<User> updateUser(string id, JsonPatchDocument<User> patchDoc) {
var fromDb = await _userCollection.Find(user => user.Id == id).FirstOrDefaultAsync();
var filter = _userCollection.Find(user => user.Id == id).FirstOrDefaultAsync();
patchDoc.ApplyTo(fromDb,ModelState);
_userCollection.UpdateOne(filter,fromDb);
var user = await _userCollection.Find(user => user.Id == id).FirstOrDefaultAsync();
return user;
}
}
UserController.cs
using Microsoft.AspNetCore.Mvc;
using backend.Services;
using backend.Models;
using Microsoft.AspNetCore.JsonPatch;
namespace backend.Controllers;
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
private readonly UserService _userService;
public UserController(UserService userservice)
{
_userService = userservice;
}
public string err = "Invalid Credentials";
[HttpGet]
public async Task<List<User>> Get() =>
await _userService.GetAsync();
[HttpGet("{id:length(24)}")]
public async Task<ActionResult<User>> Get(string id)
{
var user = await _userService.GetAsync(id);
if(user is null){
return NotFound();
}
return user;
}
[HttpPost("Login")]
public User userLogin([FromBody] Login login)
{
User usr = _userService.Login(login.email,login.password);
if(usr != null){
return usr;
}else {
return null;
}
}
// Creating New User
[HttpPost]
public async Task<IActionResult> Post(User newUser)
{
var usr = newUser;
var hashed = BCrypt.Net.BCrypt.HashPassword(newUser.Password);
usr.Password = hashed;
await _userService.CreateUser(usr);
return CreatedAtAction(nameof(Get), new { id = usr.Id }, usr);
}
//Update User
// [HttpPatch("up/{id:length(24)}")]
// public Task<User> update(string id,[FromBody] JsonPatchDocument<User> patchDoc)
// {
// var usr = _userService.GetAsync(id);
// return _userService.updateUser(id,patchDoc);
// }
}

Blazored.LocalStorage.ILocalStorageService.GetItemAsync<!0>(string))

I am new for .NET6 and I have created blazor WASM application in .NET 6.its authentication part is JWT authentication. when I am going to store the token into local storage following error is occurred.
blazor.webassembly.js:1 System.AggregateException: One or more errors occurred. (Method not found: System.Threading.Tasks.ValueTask1<!!0> Blazored.LocalStorage.ILocalStorageService.GetItemAsync<!0>(string)) ---> System.MissingMethodException: Method not found: System.Threading.Tasks.ValueTask1<!!0>
Blazored.LocalStorage.ILocalStorageService.GetItemAsync<!0>(string)
at System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start[d__8](d__8& stateMachine) in System.Private.CoreLib.dll:token 0x600454a+0x28
at Infrastructure.Manager.Preferences.ClientPreferenceManager.GetPreference() in Infrastructure.dll:token 0x600004d+0xc
at BlSls.Client.Program.Main(String[] args) in BlSls\Client\Program.cs:line 36
--- End of inner exception stack trace ---
my program.cs
public static async Task Main(string[] args)
{
var builder = WebAssemblyHostBuilder.CreateDefault(args).AddRootComponents().AddClientServices();
var host = builder.Build();
var storageService = host.Services.GetRequiredService<ClientPreferenceManager>();
if (storageService != null)
{
CultureInfo culture;
var preference = await storageService.GetPreference() as ClientPreference;
}
builder.Services.AddMudServices();
builder.Services.AddTransient<ILocalItemStore,LocalItemStore>();
builder.Services.AddSingleton<IDataConnect, DataConnect>();
builder.Services.AddScoped<Service.InterData>();
await builder.Build().RunAsync();
}
ILocalStorageService.cs
public interface ILocalStorageService
{
event EventHandler<ChangingEventArgs> Changing;
event EventHandler<ChangedEventArgs> Changed;
ValueTask ClearAsync();
ValueTask<bool> ContainKeyAsync(string key);
ValueTask<string> GetItemAsStringAsync(string key);
ValueTask<T> GetItemAsync<T>(string key);
ValueTask<string> KeyAsync(int index);
ValueTask<int> LengthAsync();
ValueTask RemoveItemAsync(string key);
ValueTask SetItemAsync<T>(string key, T data);
}
required DI in import.razor
#inject BL10StateProvider _stateProvider
#inject IAuthenticationManager _authenticationManager
login.razor
<div class="d-block">
<EditForm Model="#_tokenModel" OnValidSubmit="SubmitAsync">
<DataAnnotationsValidator />
<div class="#(f ? "invalid-box":"user-box")">
<InputText placeholder="Username" #bind-Value="_tokenModel.UserName" />
<ValidationMessage For="() => _tokenModel.UserName" />
#*<label class="body-default">Email</label>*#
</div>
<div class="#(f ? "invalid-box":"user-box")">
<InputText type="password" placeholder="Password" #bind-Value="_tokenModel.Password" />
<ValidationMessage For="() => _tokenModel.Password" />
#*<label class="body-default">Password</label>*#
</div>
<div>
<button type="submit" class="btn-block login-btn button-text"> Sign In</button>
</div>
</EditForm>
</div>
login.razor.cs
public partial class Login
{
bool f = false;
private TokenRequest _tokenModel = new();
protected override async Task OnInitializedAsync()
{
var state = await _stateProvider.GetAuthenticationStateAsync();
if (state != new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity())))
{
_navigationManager.NavigateTo("/login");
}
await Task.FromResult(0);
}
private async Task SubmitAsync()
{
var result = await _authenticationManager.Login(_tokenModel);
if (result.Succeeded)
{
f = false;
_navigationManager.NavigateTo("/CompanySelection", true);
}
else
{
_tokenModel.Messaage = "UserName Or Password Incorrect";
f = true;
}
}
}
BL10StateProvider.cs
public class BL10StateProvider {
public override async Task<AuthenticationState> GetAuthenticationStateAsync()
{
var savedToken = await _localStorage.GetItemAsync<string>(StorageConstants.Local.AuthToken);
if (string.IsNullOrWhiteSpace(savedToken))
{
return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
}
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", savedToken);
var state = new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(GetClaimsFromJwt(savedToken), "jwt")));
AuthenticationStateUser = state.User;
return state;
}
}
AuthenticationManager.cs
public class AuthenticationManager{
public async Task<IResult> Login(TokenRequest model)
{
HttpResponseMessage response=null;
try
{
using (var httpClient = new HttpClient())
{
using (var request = new HttpRequestMessage(new HttpMethod("POST"), TokenEndpoints.Authenticate))
{
request.Headers.TryAddWithoutValidation("Timestamp", DateTime.Now.Ticks.ToString());
request.Content = JsonContent.Create(model);
request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
response = await httpClient.SendAsync(request);
}
}
}
catch (Exception exp)
{
model.Messaage = "Login Falied";
model.IsTokenRequestFailed = true;
return await Result.FailAsync("Server Or API Host Not found");
}
//HttpRequestMessage req = new HttpRequestMessage();
//req.Method = HttpMethod.Post;
//req.RequestUri = TokenEndpoints.Authenticate;
//req.
////var response = await _httpClient.PostAsJsonAsync(TokenEndpoints.Authenticate, model);
// var response = await _httpClient.PostAsJsonAsync()
string responseBody = await response.Content.ReadAsStringAsync();
TokenResponse tokenRequest = JsonConvert.DeserializeObject<TokenResponse>(responseBody);
//var result = await response.ToResult<TokenResponse>();
if (tokenRequest.IsSuccess)
{
model.IsTokenRequestFailed = false;
model.Messaage = "Login Successful";
var token = tokenRequest.Token;
var refreshToken = tokenRequest.RefreshToken;
var userImageURL = tokenRequest.UserImageURL;
await _localStorage.SetItemAsync(StorageConstants.Local.AuthToken, token);
await _localStorage.SetItemAsync(StorageConstants.Local.RefreshToken, refreshToken);
if (!string.IsNullOrEmpty(userImageURL))
{
await _localStorage.SetItemAsync(StorageConstants.Local.UserImageURL, userImageURL);
}
((BL10StateProvider)this._authenticationStateProvider).MarkUserAsAuthenticated(model.UserName);
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
return await Result.SuccessAsync();
}
else
{
model.Messaage = "Login Falied";
model.IsTokenRequestFailed = true ;
return await Result.FailAsync(tokenRequest.Messages);
}
}
}
please help me to solve this issue using above code sample.
When you are in Main(), before the .RunAsync(), the JavaScript is not yet loaded.
The error is in this line:
var preference = await storageService.GetPreference() as ClientPreference;
Whatever logic you have around this has to be executed later.
One possible place is in the App component, in OnAfterRenderAsync(true):
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
var preference = await storageService.GetPreference() as ClientPreference;
...
}
}
But be aware that you first page may already be Initializing and Rendering before you have set that preference.

SSE "data" field not being received by Dart http.client

I'm building a Flutter app that receives SSE from a server and translates them to specific notifications. The server is a Spring Boot app returning events containing "event:" and "data:" fields:
public void pushNotification(String username, PushEvent event) {
var emitter = emitters.get(username);
if (emitter == null) {
return;
}
try {
emitter.send(event.toSseEvent());
} catch (IOException e) {
log.debug("Could not send event for user " + username);
emitters.remove(username);
}
}
public class PushEvent {
private String type;
private Map<String, Object> body;
public SseEmitter.SseEventBuilder toSseEvent() {
return SseEmitter.event().name(type).data(body);
}
}
On the Flutter app, I use the Dart http package to open a Stream and receive the events:
Future<void> subscribe() async {
if (!_userModel.hasAuthentication()) {
return;
}
var user = _userModel.user as AuthenticatedUser;
var username = user.username;
var token = _userModel.getToken();
var uri = Uri.https(ApiUtils.API_BASE, '/api/push/subscribe/$username');
try {
var client = http.Client();
_client = client;
var request = new http.Request("GET", uri);
request.headers["Accept"] = "text/event-stream";
request.headers["Cache-Control"] = "no-cache";
request.headers["Authorization"] = token;
var response = await client.send(request);
if (response.statusCode == 200) {
_isSubscribed = true;
response.stream.toStringStream().forEach((value) {
var event = ServerEvent.parse(value);
_handleEvents(event);
}).onError((error, stackTrace) {
log.info("Connection closed");
log.info(error);
log.info(stackTrace);
unsubscribe();
}).whenComplete(() {
log.info("Connection completed");
unsubscribe();
subscribe();
});
} else {
_isSubscribed = false;
}
notifyListeners();
} catch (e) {
unsubscribe();
log.warning("Could not subscribe to notifications");
log.warning(e);
}
}
However, when I receive an event containing data from the server, the data does not show on the log:
I/flutter (14779): event:FRIEND_REQUEST
I/flutter (14779): data:
I am certain the data is being sent by the server since the React app on the same domain decodes the SSE and shows the notifications as intended:
const subscribePush = () => {
const username = sessionStorage.getItem('loggedUsername');
const token = sessionStorage.getItem('token');
var es = new EventSourcePolyfill(
'/api/push/subscribe/' + username,
{
headers: {
"Authorization": token,
}
}
);
es.onerror = () => es.close();
es.addEventListener("FRIEND_REQUEST", e => handleFriendRequestEvent(e));
es.addEventListener("FRIEND_ACCEPT", e => handleFriendAcceptEvent(e));
}
const handleFriendRequestEvent = function (event) {
const username = sessionStorage.getItem("loggedUsername");
const data = JSON.parse(event.data);
const source = data.source;
if (source !== username) {
var note = `${source} solicitou sua amizade!`;
var newNotifs = notifications.concat(note);
setNotifications(newNotifs);
setNewNotifications(newNotifications + 1);
}
}
Could something be missing from the request on the Flutter app, or is it possibly a bug?
Your implementation looks strangely similar to this one:
https://github.com/stevenroose/dart-eventsource
Take a look at the client implementation and how the response in decoded using the decoder.dart file.

Insert Image to database using typescript (angular2) and spring boot (spring data)

I start by presenting my client side the service
addImage (url: string, params: string[], files: File[]): Observable {
return Observable.create(observer => {
let formData: FormData = new FormData(),
xhr: XMLHttpRequest = new XMLHttpRequest();
for (let i = 0; i < files.length; i++) {
formData.append("uploads[]", files[i], file
s[i].name);
}
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
observer.next(JSON.parse(xhr.response));
observer.complete();
} else {
observer.error(xhr.response);
}
}
};
xhr.upload.onprogress = (event) => {
this.progress = Math.round(event.loaded / event.total * 100);
this.progressObserver.next(this.progress);
};
xhr.open('POST', url, true);
xhr.send(formData);
});
}
then this is my html code
<input type="file" (change)="uploadImage($event)"/>
where I call this method from my component
uploadImage(event) {
var files = event.srcElement.files;
console.log(files);
this._serviceSection.addImage('http://localhost:8080/template/img', [], files).subscribe(() => {
console.log('sent');
});
}
and in my service side
this is a method from my controller
#RequestMapping(value = "/img", method = RequestMethod.POST)
public void getFileContents(#RequestParam MultipartFile file) {
System.out.println("++++++++++++++++++++++++++++++++++++++++ " +file.getOriginalFilename());
}
in a first time a try just to show a fileName but I get this error
It seems that your progressObserver isn't set. The reason for this could be that you didn't subscribe to its associated observable. Don't forget that observables are lazy and if you don't subscribe to them, their initialization callback isn't called.
To prevent from having the error, you could check if it's null or not:
xhr.upload.onprogress = (event) => {
if (this.progressObserver) {
this.progress = Math.round(event.loaded / event.total * 100);
this.progressObserver.next(this.progress);
}
};
Otherwise, you can notice that from RC2, Angular2 accepts FormData objects as parameters of HTTP methods...