Flutter PWA service worker fails to successfully register - flutter

I've built my flutter app and have the pwa version of it, hosted it on windows server IIS 10 with a valid ssl certificate on subdomain like Https://pwa.mydomain.com, but when I visit the page, it doesn't show the Install app button on chrome. How can I solve this?
What I've done so far:
Removed the references to files that did not exist anymore in the project.
Here is my service worker:
'use strict';
const MANIFEST = 'flutter-app-manifest';
const TEMP = 'flutter-temp-cache';
const CACHE_NAME = 'flutter-app-cache';
const RESOURCES = {
".dart_tool/package_config.json": "23791ac6df06edd3cb7ca99ab6949a5a",
".dart_tool/package_config_subset": "8453ab6a2adbaeda61d2d58965841223",
".dart_tool/version": "a13ed25c1389038783693bec771e71f6",
"assets/assets/images/svgIcon/wifiOff.svg": "ca97536b42e250214b667a606455a58f",
"assets/FontManifest.json": "fba83cca6cbe4083e6cc0498e15a1a6f",
"assets/fonts/MaterialIcons-Regular.otf": "4e6447691c9509f7acdbf8a931a85ca1",
"assets/NOTICES": "0e94f133a57e1e05b650363a497d95b0",
"assets/packages/cupertino_icons/assets/CupertinoIcons.ttf": "6d342eb68f170c97609e9da345464e5e",
"favicon.png": "19e188c1100dbd706d3d81309d478996",
"icons/launcher_logo192.png": "ea22e4258d33a9a184a426b38f3e6f46",
"icons/launcher_logo512.png": "19e188c1100dbd706d3d81309d478996",
"index.html": "fde3d71f9f8a4623bd98e956040169d2",
"/": "fde3d71f9f8a4623bd98e956040169d2",
"lib/main.dart": "5515740aeb62c25aa7692184d00b90b1",
"lib/src/qr_code_scanner_web.dart": "50b70d762e80fedd228974e786e8f38a",
"lib/src/qr_code_scanner_web_impl.dart": "a0f12d21cc75aefe84f6fbf9c1e0307e",
"lib/src/qr_code_scanner_web_impl_no_web.dart": "11e3ab6339d41ded6e650c96120e7560",
"main.dart.js": "6161190dc35f953037fb5c0e7db4f0e6",
"manifest.json": "a281fd45bea620b437523746b29b93d2",
"pubspec.lock": "6c06cc012f6524824b77f7286b6e256e",
"pubspec.yaml": "b36c6ef43dcaa5a79fc412a8368306cf",
"version.json": "cbca785ad23c1984a075073af0a61003"
//Removed for brevity
};
// The application shell files that are downloaded before a service worker can
// start.
const CORE = [
"/",
"main.dart.js",
"index.html",
"assets/NOTICES",
"assets/AssetManifest.json",
"assets/FontManifest.json"];
// During install, the TEMP cache is populated with the application shell files.
self.addEventListener("install", (event) => {
self.skipWaiting();
return event.waitUntil(
caches.open(TEMP).then((cache) => {
return cache.addAll(
CORE.map((value) => new Request(value, {'cache': 'reload'})));
})
);
});
// During activate, the cache is populated with the temp files downloaded in
// install. If this service worker is upgrading from one with a saved
// MANIFEST, then use this to retain unchanged resource files.
self.addEventListener("activate", function(event) {
return event.waitUntil(async function() {
try {
var contentCache = await caches.open(CACHE_NAME);
var tempCache = await caches.open(TEMP);
var manifestCache = await caches.open(MANIFEST);
var manifest = await manifestCache.match('manifest');
// When there is no prior manifest, clear the entire cache.
if (!manifest) {
await caches.delete(CACHE_NAME);
contentCache = await caches.open(CACHE_NAME);
for (var request of await tempCache.keys()) {
var response = await tempCache.match(request);
await contentCache.put(request, response);
}
await caches.delete(TEMP);
// Save the manifest to make future upgrades efficient.
await manifestCache.put('manifest', new Response(JSON.stringify(RESOURCES)));
return;
}
var oldManifest = await manifest.json();
var origin = self.location.origin;
for (var request of await contentCache.keys()) {
var key = request.url.substring(origin.length + 1);
if (key == "") {
key = "/";
}
// If a resource from the old manifest is not in the new cache, or if
// the MD5 sum has changed, delete it. Otherwise the resource is left
// in the cache and can be reused by the new service worker.
if (!RESOURCES[key] || RESOURCES[key] != oldManifest[key]) {
await contentCache.delete(request);
}
}
// Populate the cache with the app shell TEMP files, potentially overwriting
// cache files preserved above.
for (var request of await tempCache.keys()) {
var response = await tempCache.match(request);
await contentCache.put(request, response);
}
await caches.delete(TEMP);
// Save the manifest to make future upgrades efficient.
await manifestCache.put('manifest', new Response(JSON.stringify(RESOURCES)));
return;
} catch (err) {
// On an unhandled exception the state of the cache cannot be guaranteed.
console.error('Failed to upgrade service worker: ' + err);
await caches.delete(CACHE_NAME);
await caches.delete(TEMP);
await caches.delete(MANIFEST);
}
}());
});
// The fetch handler redirects requests for RESOURCE files to the service
// worker cache.
self.addEventListener("fetch", (event) => {
if (event.request.method !== 'GET') {
return;
}
var origin = self.location.origin;
var key = event.request.url.substring(origin.length + 1);
// Redirect URLs to the index.html
if (key.indexOf('?v=') != -1) {
key = key.split('?v=')[0];
}
if (event.request.url == origin || event.request.url.startsWith(origin + '/#') || key == '') {
key = '/';
}
// If the URL is not the RESOURCE list then return to signal that the
// browser should take over.
if (!RESOURCES[key]) {
return;
}
// If the URL is the index.html, perform an online-first request.
if (key == '/') {
return onlineFirst(event);
}
event.respondWith(caches.open(CACHE_NAME)
.then((cache) => {
return cache.match(event.request).then((response) => {
// Either respond with the cached resource, or perform a fetch and
// lazily populate the cache.
return response || fetch(event.request).then((response) => {
cache.put(event.request, response.clone());
return response;
});
})
})
);
});
self.addEventListener('message', (event) => {
// SkipWaiting can be used to immediately activate a waiting service worker.
// This will also require a page refresh triggered by the main worker.
if (event.data === 'skipWaiting') {
self.skipWaiting();
return;
}
if (event.data === 'downloadOffline') {
downloadOffline();
return;
}
});
// Download offline will check the RESOURCES for all files not in the cache
// and populate them.
async function downloadOffline() {
var resources = [];
var contentCache = await caches.open(CACHE_NAME);
var currentContent = {};
for (var request of await contentCache.keys()) {
var key = request.url.substring(origin.length + 1);
if (key == "") {
key = "/";
}
currentContent[key] = true;
}
for (var resourceKey of Object.keys(RESOURCES)) {
if (!currentContent[resourceKey]) {
resources.push(resourceKey);
}
}
return contentCache.addAll(resources);
}
// Attempt to download the resource online before falling back to
// the offline cache.
function onlineFirst(event) {
return event.respondWith(
fetch(event.request).then((response) => {
return caches.open(CACHE_NAME).then((cache) => {
cache.put(event.request, response.clone());
return response;
});
}).catch((error) => {
return caches.open(CACHE_NAME).then((cache) => {
return cache.match(event.request).then((response) => {
if (response != null) {
return response;
}
throw error;
});
});
})
);
}
This is the error I'm getting:
Uncaught (in promise) TypeError: Failed to execute 'addAll' on 'Cache': Request failed
I'm curious whether this error has something to do with CORS or not.
Thanks.

Related

Service worker returning Offline page instead of 404 page when non-existant file is requested

I'm using this service worker for caching and offline mode.
when I am already on any existing page of my website and I request a non-existent page, the Offline page is served instead of the 404 page.
If, on the other hand, I close and reopen the browser and immediately request a non-existent page of my website, the 404 page is served correctly.
I suspect it has to do with these last few lines of the code
if(!matching || matching.status == 404) { return cache.match("/service/offline/"); but i don't know how to fix this problem. Can you help me please?
Here's the full service worker:
self.addEventListener("install", function(event) {
event.waitUntil(preLoad());
});
var preLoad = function(){
console.log("Installing web app");
return caches.open("offline").then(function(cache) {
console.log("caching index and important routes");
return cache.addAll([
'/assets/css/about.min.css',
'favicon.ico',
'/assets/js/script.js',
'manifest.webmanifest.webmanifest',
'/',
'/about/',
'/contact/',
'/service/offline/'
]);
});
};
self.addEventListener("fetch", function(event) {
event.respondWith(checkResponse(event.request).catch(function() {
return returnFromCache(event.request);
}));
event.waitUntil(addToCache(event.request));
});
var checkResponse = function(request){
return new Promise(function(fulfill, reject) {
fetch(request).then(function(response){
if(response.status !== 404) {
fulfill(response);
} else {
reject();
}
}, reject);
});
};
var addToCache = function(request){
return caches.open("offline").then(function (cache) {
return fetch(request).then(function (response) {
console.log(response.url + " was cached");
return cache.put(request, response);
});
});
};
var returnFromCache = function(request){
return caches.open("offline").then(function (cache) {
return cache.match(request).then(function (matching) {
if(!matching || matching.status == 404) {
return cache.match("/service/offline/");
} else {
return matching;
}
});
});
};

Why is Flutter Dart application throwing an XMLHttpRequest error when test programme and POSTMAN do not?

I have an application that was working doing http.get requests on an Ubuntu virtual machine hosting a postgres DB and an API using Hapi/node. The VM disk become corrpupted and following the rebuild, the http.get now throws an XMLHttpRequest error. However, a test programme doing the same request works fine and a test GET using POSTMAN works fine as well. I'm stumped as to why this is the case.
The code that is throwing the error is as follows (the getScores() function):
import 'package:noxo/functions.dart' as func;
import 'dart:convert';
import 'package:http/http.dart' as http;
class DataManager {
Map<dynamic, dynamic> results = {}; // Return values from DB parameter query
double difficulty = 5; // Level of difficulty (1- easiest to 5-hardest)
// Address of API for data requests
final _apiAddress = 'http://192.168.1.201:4044/';
bool _httpAvailable = true; // Assume that http is available
int getHumanScore() {
func.debugPrint('httpAvailable is $_httpAvailable');
if (_httpAvailable && results.isNotEmpty) {
func.debugPrint('Returning HUMAN score');
return results['humanwin'];
} else {
return 0;
}
}
int getDrawScore() {
if (_httpAvailable && results.isNotEmpty) {
return results['draws'];
} else {
return 0;
}
}
int getComputerScore() {
if (_httpAvailable && results.isNotEmpty) {
return results['computerwin'];
} else {
return 0;
}
}
void setDifficulty(double value) {
func.debugPrint('Set difficulty = $value');
difficulty = value;
}
double getDifficulty() {
func.debugPrint('Get difficulty is $difficulty');
return difficulty;
}
void getScores(Function() updateScores) async {
if (_httpAvailable) {
// If we haven't had a previous http error - read the scores
try {
dynamic _parsedAddress = Uri.parse(_apiAddress);
final response = await http.get(_parsedAddress);
func.debugPrint(
'doing getScores. Address = $_apiAddress. statusCode = ${response.statusCode}');
Map decodedResponse = jsonDecode(response.body) as Map;
if (response.statusCode == 200) {
func.debugPrint('getScores response: $decodedResponse');
results = decodedResponse;
updateScores(); // Update scores on main.dart
} else {
throw Exception('Unable to fetch products from the REST API');
}
} catch (e) {
func.debugPrint('getScores. Error is $e.');
_httpAvailable = false; // Disable http checks because we had an error
results =
{}; // Return an empty map if the internet connection is not available
}
}
}
// Put data about scores into the database
Future<void> putScores(String resultPath) async {
// Only try to put the scores if http is available
if (_httpAvailable) {
try {
String address = '$_apiAddress$resultPath';
func.debugPrint('http address: $address');
var response = await http.post(Uri.parse(address));
func.debugPrint('http response: ${response.body.toString()}');
// Check for sucess, throw an error if it didn't work
if (response.statusCode == 200 ||
response.statusCode == 201 ||
response.statusCode == 204) {
return;
} else {
throw Exception(
'Unable to update results from the REST API. Status Code: ' +
response.statusCode.toString());
}
} catch (e) {
_httpAvailable = false; // Disable http requests
}
}
}
}
The output when "getScores()" is called is:
getScores. Error is XMLHttpRequest error..
The Hapi interface code is as follows:
'use strict';
const Hapi = require('#hapi/hapi');
const { options } = require('#hapi/hapi/lib/cors');
const Inert = require('#hapi/inert');
const HapiPostgresConnection = require('hapi-postgres-connection');
const path = require('path');
const debug = true;
const init = async () => {
const server = Hapi.server({
port: 4044,
host: '192.168.1.201',
//host: '0.0.0.0',
routes: {
cors: false
/*
{
origin: ['192.168.*'], // an array of origins or 'ignore'
headers: ['Authorization'], // an array of strings - 'Access-Control-Allow-Headers'
exposedHeaders: ['Accept'], // an array of exposed headers - 'Access-Control-Expose-Headers',
additionalExposedHeaders: ['Accept'], // an array of additional exposed headers
maxAge: 60,
credentials: false // boolean - 'Access-Control-Allow-Credentials'
*/
}
});
await server.register([{
plugin: HapiPostgresConnection
},
{
plugin: Inert
}]);
server.route({
method: 'GET',
path: '/',
handler: async function (request, h) {
let id = '1';
let statement = `SELECT * FROM scores WHERE id = ${id}`;
debugPrint(`Doing GET. Statement = ${statement}`);
try {
const result = await request.pg.client.query(statement);
debugPrint(`After GET. Result = ${result}. Response = ${h.response()}`);
return h.response(result.rows[0]);
} catch (err) {
console.log(err);
}
}
});
server.route({
method: 'GET',
path: '/noxo',
handler: (request, h) => {
return h.file('/home/mike/programming/noxo/index.html');
}
});
server.route({
method: 'GET',
path: '/fwebtest',
handler: (request, h) => {
return h.file('index.html', options [{confine: false}]);
},
options: {
files: {
relativeTo: path.join(__dirname, 'fwebtest')
},
},
});
server.route({
method: 'GET',
path: '/webtest',
handler: (request, h) => {
return h.file('./webtest/index.html', options [{confine: false}])
}
});
server.route({
method: ['PUT', 'POST'],
path: '/',
handler: async function (request, h) {
let statement = 'update scores set';
var jsonData = request.payload;
if (jsonData.hasOwnProperty('id')) {
delete jsonData.id;
}
var first = true
for (var key of Object.keys(jsonData)) {
if (!first) {
statement = statement + ",";
}
statement = statement + ' ' + key + ' = ' + jsonData[key];
first = false;
}
statement = statement + ' where id = 1'
debugPrint(`Doing PUT. Statement = ${statement}`);
try {
const result = await request.pg.client.query(statement);
return h.response(result.rows[0]);
} catch (err) {
console.log(err);
}
}
});
await server.start();
console.log('Server running on %s', server.info.uri);
};
function buildStatement(element, index, array) {
if (index != 'id') {
statement = statement + index + ' = ' + value + ' ';
}
return this;
}
function debugPrint(value){
if (debug == true){
console.log(value);
}
}
process.on('unhandledRejection', (err) => {
console.log(err);
process.exit(1);
});
init();
The test dart programme, which works, is as follows:
import 'dart:convert';
import 'package:http/http.dart' as http;
// Address of API for data requests
final _apiAddress = 'http://192.168.1.201:4044/';
// Global variables
bool _httpAvailable = true; // Has http conect worked? Assume yes.
const bool debug = true; // Global bebug print variable.
void main() async {
// Get the scores from the API
Map scores = await _getScores();
}
// Function to get the scores using http command
Future<Map> _getScores() async {
debugPrint('Doing GET...');
dynamic response;
Map decodedResponse = {};
try {
dynamic _parsedAddress = Uri.parse(_apiAddress);
response = await http.get(_parsedAddress);
decodedResponse = jsonDecode(response.body) as Map;
} catch (e) {
debugPrint('getScores. Error is $e.');
_httpAvailable = false; // Disable http checks because we had an error
decodedResponse =
{}; // Return an empty map if the internet connection is not available
}
if (response.statusCode == 200) {
debugPrint('response.body:');
debugPrint(response.body);
debugPrint('GET successful... code: ' + response.statusCode.toString());
debugPrint(' ');
return decodedResponse;
} else {
throw Exception('Unable to fetch products from the REST API. Error code: ' +
response.statusCode.toString());
}
}
debugPrint(String message) {
if (debug == true) {
print(message);
}
}
The output from this programme is:
D:\Sync\Programming\Flutter\http_get>dart run
Building package executable...
Built http_test:http_test.
Doing GET...
response.body:
{"id":1,"humanwin":3,"draws":0,"computerwin":0}
GET successful... code: 200
The POSTMAN results are:
I assume that this is a CORS issue from what I have read about XMLHttpRequest errors, but believe I have disabled CORS in the Hapi interface. The application class, test programme and POSTMAN test all pre-date the Ubuntu VM rebuild so the only thing that has changed is the VM. The Hapi code was backed up so that shouldn't have changed either, although I can't quite remember when I took the last backup.
Does anyone have any ideas about why the test programme and POSTMAN work, but my DataManager class in my application doesn't?
In the routes definition for Hapi, cors needs to be set to true as below.
const server = Hapi.server({
port: xxxx,
host: 'xxx.xxx.xxx.xxx',
routes: {
cors: true
}
});

PWA problem with Vue3+service worker+keep-alive

I have a problem with Vue3+service worker+keep-alive.
I use keep-live in template
<q-page-container>
<router-view v-slot="{ Component }">
<keep-alive :include="['WorkPage']">
<component :is="Component" :key="$route.fullPath"/>
</keep-alive>
</router-view>
</q-page-container>
create queue
createWorkQueue = new Queue('createWorkQueue', {
onSync: async ( {queue} ) => {
let entry
while (entry = await queue.shiftRequest()) {
try {
await fetch(entry.request);
const channel = new BroadcastChannel('sw-messages-work');
channel.postMessage({msg: 'offline-work-uploaded'});
} catch (error) {
await queue.unshiftRequest(entry);
throw error;
}
}
}
})
addEventListener('fetch'
self.addEventListener('fetch', (event) => {
if (event.request.url.endsWith('/api/ins_new_work')) {
const bgSyncLogic = async () => {
try {
const response = await fetch(event.request.clone())
return response
} catch (error) {
await createWorkQueue.pushRequest({request: event.request})
return error
}
}
event.respondWith(bgSyncLogic())
}
})
when in offline I send form - createWorkQueue.pushRequest hangs to 5 minutes
if I delete from keep-alive - WorkPage - then pushRequest works well
but I need keep-alive page. How can I solve this?
I found!!
I use IndexedDB library and for show offline message I read from DB information
const db = await openDB('workbox-background-sync')
but in first time - table 'requests' don't create
I insert next code
const db = await openDB('workbox-background-sync', undefined, { upgrade(db) { db.createObjectStore('requests') }})
and works well

AWS Lambda - MongoDB resource optimization

I'm building facebook chatbot using AWS Lambda and MongoDB. At the moment, my application is pretty simple but I'm trying to nail down the basics before I move onto the complex stuff.
I understand AWS Lambda is stateless but I've read adding below line in handler along with variables initialized outside handler, I don't have to establish DB connection on every request.
context.callbackWaitsForEmptyEventLoop = false;
(I've read this from this article; https://www.mongodb.com/blog/post/optimizing-aws-lambda-performance-with-mongodb-atlas-and-nodejs)
I'm adding my entire code below
'use strict'
const
axios = require('axios'),
mongo = require('mongodb'),
MongoClient = mongo.MongoClient,
assert = require('assert');
var VERIFY_TOKEN = process.env.VERIFY_TOKEN;
var PAGE_ACCESS_TOKEN = process.env.PAGE_ACCESS_TOKEN;
var MONGO_DB_URI = process.env.MONGO_DB_URI;
let cachedDb = null;
let test = null;
exports.handler = (event, context, callback) => {
var method = event.context["http-method"];
context.callbackWaitsForEmptyEventLoop = false;
console.log("test :: " + test);
if (!test) {
test = "1";
}
// process GET request --> verify facebook webhook
if (method === "GET") {
var queryParams = event.params.querystring;
var rVerifyToken = queryParams['hub.verify_token']
if (rVerifyToken === VERIFY_TOKEN) {
var challenge = queryParams['hub.challenge'];
callback(null, parseInt(challenge))
} else {
var response = {
'body': 'Error, wrong validation token',
'statusCode': 403
};
callback(null, response);
}
// process POST request --> handle message
} else if (method === "POST") {
let body = event['body-json'];
body.entry.map((entry) => {
entry.messaging.map((event) => {
if (event.message) {
if (!event.message.is_echo && event.message.text) {
console.log("BODY\n" + JSON.stringify(body));
console.log("<<MESSAGE EVENT>>");
// retrieve message
let response = {
"text": "This is from webhook response for \'" + event.message.text + "\'"
}
// facebook call
callSendAPI(event.sender.id, response);
// store in DB
console.time("dbsave");
storeInMongoDB(event, callback);
}
} else if (event.postback) {
console.log("<<POSTBACK EVENT>>");
} else {
console.log("UNHANDLED EVENT; " + JSON.stringify(event));
}
})
})
}
}
function callSendAPI(senderPsid, response) {
console.log("call to FB");
let payload = {
recipient: {
id: senderPsid
},
message: response
};
let url = `https://graph.facebook.com/v2.6/me/messages?access_token=${PAGE_ACCESS_TOKEN}`;
axios.post(url, payload)
.then((response) => {
console.log("response ::: " + response);
}).catch(function(error) {
console.log(error);
});
}
function storeInMongoDB(messageEnvelope, callback) {
console.log("cachedDB :: " + cachedDb);
if (cachedDb && cachedDb.serverConfig.isConnected()) {
sendToAtlas(cachedDb.db("test"), messageEnvelope, callback);
} else {
console.log(`=> connecting to database ${MONGO_DB_URI}`);
MongoClient.connect(MONGO_DB_URI, function(err, db) {
assert.equal(null, err);
cachedDb = db;
sendToAtlas(db.db("test"), messageEnvelope, callback);
});
}
}
function sendToAtlas(db, message, callback) {
console.log("send to Mongo");
db.collection("chat_records").insertOne({
facebook: {
messageEnvelope: message
}
}, function(err, result) {
if (err != null) {
console.error("an error occurred in sendToAtlas", err);
callback(null, JSON.stringify(err));
} else {
console.timeEnd("dbsave");
var message = `Inserted a message into Atlas with id: ${result.insertedId}`;
console.log(message);
callback(null, message);
}
});
}
I did everything as instructed and referenced a few more similar cases but somehow on every request, "cachedDb" value is not saved from previous request and the app is establishing the connection all over again.
Then I also read that there is no guarantee the Lambda function is using the same container on multiple requests so I made another global variable "test". "test" variable value is logged "1" from the second request which means it's using the same container but again, "cachedDb" value is not saved.
What am I missing here?
Thanks in advance!
In short AWS Lambda function is not a permanently running service of any kind.
So, far I know AWS Lambda works on idea - "one container processes one request at a time".
It means when request comes and there is available running container for the Lambda function AWS uses it, else it starts new container.
If second request comes when first container executes Lambda function for first request AWS starts new container.
and so on...
Then there is no guarantee in what container (already running or new one) Lambda function will be executed, so... new container opens new DB connection.
Of course, there is an inactivity period and no running containers will be there after that. All will start over again by next request.

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...