Wątek przeniesiony 2023-10-21 17:17 z Inżynieria oprogramowania przez Riddle.

Kontakt przeglądarki web z systemem

0

Od niedawna zacząłem "wchodzić" w temat WebAssembly z wykorzystaniem Emscripten, porobiłem pierwsze eksperymenty z bardzo dobrymi efektami.

Bez owijania w bawełnę, przy korzystaniu ze standardowych rozwiązań przy tworzeniu aplikacji przeglądarkowych, jest następujący problem, między innymi:

  • nie jest możliwy odczyt pliku po podanej nazwie, nie jest możliwy odczyt listy pliku w folderze o podanej ścieżce.
  • nie jest możliwy bezpośredni zapis pliku. Jedynie jest możliwe wywołanie pobrania pliku.
  • nie jest możliwe wykonywanie dowolnych połączeń TCP i UDP.

Nawet tu jest napisane https://emscripten.org/docs/porting/files/index.html że Emscripten tworzy fikcyjny system plików z tego względu, że dostęp do tego prawdziwego, nie jest możliwy.

Spotkałem się z dwoma rozwiązaniami powyższego problemu:

  1. Napisanie prostej aplikacji desktopowej, w ktorym będzie osadzony "środek" przeglądarki, zwany WebBrowser, WebView itp. W większości przypadków, taki komponent pozwala wywołać kod aplikacji osadzającej z Javascript uruchomionego wewnątrz WebView, a także, odwołując się do komponentu, można wywołać dowolną funkcję Javascript. Sam odczyt i zapis plików, a także utrzymywanie połączeń zapewniała by ta aplikacja desktopowa z osadzoną przeglądarką.
  2. Napisanie prostego serwera zgodnego z WebSocket. Aplikacja HTML+JS+WASM wykonałaby połączenie do tego serwera poprzez WebSocket, natomiast serwer by odczytywał, zapisywał pliki.

Pytanie nie jest o to, jak to zrobić, bo wiem, że da się to zrobić i w razie czego poszukam na google.

Pytanie jest o to, jak robi to większość programistów. Myślę, że jestem nie pierwszy i nie ostatni z takim problemem i być może jest wypracowany jakiś standard. Czy faktycznie istnieje standardowy sposób? Czy może istnieje już gotowa aplikacja desktopowa z przeglądarką bądź serwer WebSocket na każdy komputer, na każdy telefon z przeznaczeniem do ominięcia ograniczeń przeglądarki i wystarczy skorzystać zamiast tworzyć to samo na nowo?

Trochę rozpoznałem temat i znalazłem:
https://cordova.apache.org/
https://capacitorjs.com/
https://tauri.app/
https://www.electronjs.org/

To jest prawie to, czego szukałem, realizujące podejście opisane w punkcie 1, ale główna różnica jest taka, że wymienione programy przerabiają HTML+CSS+JS na zwykłą apkę desktopową. Jak jest potrzeba coś zmienić, to znowu trzeba zapakować. Mi chodziło raczej o to, że jest gotowa aplikacja przypominająca przeglądarkę, a właściwie to będąca przeglądarką, do której zaczytuje się plik HTML lub podaje jakiś adres HTTP i mam dostęp do całego systemu poprzez dodatkowe API, które zapewnia ta aplikacja. Testowałem to podejście na przykładzie C# z WPF i da się tak zrobić, tylko z przenośnością na inny system może być problem.

Drugi sposób to, jak wspomniałem, jest Websocket, do którego podłącza się mój HTML+JS odpalony w dowolnej zwyczajnej przeglądarce takiej, jak Chrome, Firefox. Robiłem eksperymenty z takim działaniem na własny użytek i faktycznie, to da się zrobić, to działa, a serwer byłby bardzo prostą aplikacją w konsoli lub desktopową, którą można zbudować praktycznie w każdym istniejącym języku, czy to na komputer, czy to na telefon. Prościej się już chyba nie da, ale jakoś nikt przede mną nie wypuścił takiego gotowca, a przynajmniej nie jest to używane na duża skalę. Próbowałem w C# lub w Java i w obu przypadkach nawet da się uruchamiać na różnych komputerach bez przekompilowywania, a mając w Java, to zapewne dałoby się przerobić na Androida. Czy na IOS da się przerobić, to nie wiem, nie mam możliwości sprawdzić. Przy takim podejściu można mieć z jednego pliku aplikację typowo webową, pracującą bez serwera i taką pracującą z serwerem. Po prostu w przypadku pracy bez serwera Websocket, niektóre funkcje nie będą działać lub będą działać inaczej. W pradawnych czasach coś takiego można było zrealizować za pomocą apletu Java lub Flash, a jeszcze wcześniej były biblioteki ActiveX, które działały tylko w Internet Explorer.

Kiedyś czytałem o jakiś "File API" dla standardu webowego, ale to chyba nie jest szeroko używane, a jeśli w ogóle, to ma spore ograniczenia w działaniu.

0

Napisz prostą aplikację z osadzoną przeglądarka (WebBrowser, CEF albo czymś podobnym) i sam sobie udostępnij API takie jak potrzebujesz.
W takim CFM możesz przechwycić takie zdarzenia:
https://cefsharp.github.io/api/51.0.0/html/Events_T_CefSharp_OffScreen_ChromiumWebBrowser.htm

Dla JAVA: https://stackoverflow.com/questions/56707231/web-browser-control-in-java

0

Czyli chcesz zrobić przeglądarkę plików lokalnych, tylko że z przeglądarki internetowej?

0
Riddle napisał(a):

Czyli chcesz zrobić przeglądarkę plików lokalnych, tylko że z przeglądarki internetowej?

Można powiedzieć, że tak. Ja już nawet taką zrobiłem do testów z wykorzystaniem własnego serwera Websocket i ona działa. Pytanie jest raczej, czy jest jakiś przyjęty standard. Po co każdy programista miałby sam implementować własny serwer tylko po to, żeby umożliwić owe przeglądanie plików z przeglądarki?

0
4w0rX4t4X napisał(a):

Napisz prostą aplikację z osadzoną przeglądarka (WebBrowser, CEF albo czymś podobnym) i sam sobie udostępnij API takie jak potrzebujesz.
W takim CFM możesz przechwycić takie zdarzenia:
https://cefsharp.github.io/api/51.0.0/html/Events_T_CefSharp_OffScreen_ChromiumWebBrowser.htm

Dla JAVA: https://stackoverflow.com/questions/56707231/web-browser-control-in-java

Napisanie customowej apki do tego celu to może najwyżej godzina roboty i bez trudu do zrobienia, sam to przerabiałem. Wymieniłem programy, o których słyszałem i niby robią to samo w sposób automatyczny, ale bez możliwości podstawiania dowolnego HTML z pliku lub URL bez ponownej kompilacji. Tylko ciekawe, że nie jest znany gotowiec, który jest taką apką, którą uruchamia się z parametrem lub w czasiej jej pracy podaje się URL lub plik i jest.

0

Jakiś czas temu robiłem podobny projekt i jednym z pierwszych podejść (z którego musiałem zrezygnować ale może ci podpasuje) było użycie gRPC. Ogólnie to chyba najprostsze podejście jakie znalazłem

  1. Tworzysz nowy projekt ASP.NET Core gRPC Service, dodajesz paczkę Grpc.AspNetCore.Web

  2. Tworzysz plik Protos\files.proto

syntax = "proto3";

option csharp_namespace = "GrpcService1";

package filesApi;

service FilesApi {
  rpc GetFiles (GetFilesRequest) returns (GetFilesReply);
}

message GetFilesRequest {
  string path = 1;
}

message GetFilesReply {
	repeated string name = 1;
}

i serwis

using Grpc.Core;

namespace GrpcService1.Services
{
    public class FilesApiService : FilesApi.FilesApiBase
    {
        public override Task<GetFilesReply> GetFiles(GetFilesRequest request, ServerCallContext context)
        {
            var reply = new GetFilesReply();
            reply.Name.AddRange(Directory.GetFileSystemEntries(request.Path));
            return Task.FromResult(reply);
        }
    }
}

Potem tylko prosta konfiguracja serwera, ustawienia CORS i włączenie WebGRPC

using GrpcService1.Services;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors(o => o.AddPolicy("AllowAll", builder =>
{
    builder.AllowAnyOrigin()
           .AllowAnyMethod()
           .AllowAnyHeader()
           .WithExposedHeaders("Grpc-Status", "Grpc-Message", "Grpc-Encoding", "Grpc-Accept-Encoding");
}));
builder.Services.AddGrpc();

var app = builder.Build();
app.UseCors("AllowAll");
app.UseGrpcWeb(new GrpcWebOptions { DefaultEnabled = true });
app.MapGrpcService<FilesApiService>();
app.MapGet("/", () => "Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");

app.Run("https://localhost:8080");

Ogólnie wszystko jak w tym tutorialu https://github.com/grpc/grpc-web/tree/master/net/grpc/gateway/examples/helloworld#write-client-code

  1. Instalujesz protoc i pluginy gen-js, gen-grpc-web

npm install -g protoc protoc-gen-js protoc-gen-grpc-web

i odpalasz w konsoli

protoc -I=. files.proto --js_out=import_style=commonjs:. --grpc-web_out=import_style=commonjs,mode=grpcwebtext:.

żeby przerobić plik .proto na pliki .js.

  1. Tworzysz klienta w skrypcie client.js i go kompilujesz pod przeglądarkę, np:
const { GetFilesRequest } = require('./files_pb.js');
const { FilesApiClient } = require('./files_grpc_web_pb.js');

var client = new FilesApiClient('https://localhost:8080');

window.FilesApi = {
    getFiles: async function (path) {
        return new Promise((resolve, reject) => {
            var request = new GetFilesRequest();
            request.setPath(path);

            try {
                client.getFiles(request, {}, (err, response) => resolve(response.getNameList()));
            } catch (err) {
                reject(err);
            }
        });
    }
};

Kompilujesz go przez

npx webpack ./client.js

W package.json potrzebujesz

{
  "name": "grpc-web-simple-example",
  "version": "0.1.0",
  "description": "gRPC-Web simple example",
  "main": "server.js",
  "devDependencies": {
    "@grpc/grpc-js": "~1.0.5",
    "@grpc/proto-loader": "~0.5.4",
    "async": "~1.5.2",
    "google-protobuf": "~3.14.0",
    "grpc-web": "~1.4.2",
    "webpack": "~5.82.1",
    "webpack-cli": "~5.1.1"
  }
}
  1. Załączasz ten plik w htmlu i go normalnie używasz, np:
<script src="main.js"></script>
<script>
    async function getFiles() {
        const path = document.getElementById('path').value;
        document.getElementById('files').value = (await window.FilesApi.getFiles(path)).join('\n');
    }
</script>
<body>
    <input value="C:\" id="path" />
    <button type="button" onclick="getFiles()">Get files list</button>
    <textarea id="files"></textarea>
</body>

i to tyle. Musisz tylko dodać jakieś zabezpieczenia bo w tej formie potencjalnie eksponujesz zawartość swojego dysku na cały internet.
Łatwo to rozbudować, biblioteki do gRPC są do każdego języka i platformy więc możesz to przenieść na androida, maca itp.

Wspomniałeś o C# dlatego dałem kod serwera w C# ale można go napisać w dowolnym języku a język protokołu zostaje ten sam.

Ale pytanie co w ogóle próbujesz stworzyć? Aplikację desktopową którą będziesz mógł pisać jak webową? Może szukasz czegoś takiego jak ElectronJS / react native?

0

i to tyle. Musisz tylko dodać jakieś zabezpieczenia bo w tej formie potencjalnie eksponujesz zawartość swojego dysku na cały internet.
Łatwo to rozbudować, biblioteki do gRPC są do każdego języka i platformy więc możesz to przenieść na androida, maca itp.

Dziękuję za propozycję, nie wiedziałem nic o gRPC, sprawdzę to. Na pierwszy rzut oka to chyba to samo co WebSocket lub HTTP, tylko inny protokół. Mając serwer WebSocket też można wystawić dysk na cały internet ze wszelkimi tego konsekwencjami. Gdzieś czytałem, że standardowa przeglądarka potrafi się łączyć albo poprzez WebSocket, albo jako żądanie-odpowiedź w protokole HTTP, inaczej się nie da, ale to i tak już otwiera możliwość dotarcia przeglądarce do całego OS za pośrednictwem serwera.

Ale pytanie co w ogóle próbujesz stworzyć? Aplikację desktopową którą będziesz mógł pisać jak webową? Może szukasz czegoś takiego jak ElectronJS / react native?

Niedawno "wszedłem" w WebAssembly już poznałem jak to działa, co jest do tego potrzebne, język C++ też znam dobrze. Właśnie chodzi o przerobienie kilku aplikacji desktopowych (C++ z Qt, C# z WinForms lub w konsoli, Java z Swing) na webowe z wykorzystaniem WASM, w miarę możliwości tak, żeby nie potrzebowała takich "dodatków", ale czasami tego się nie uniknie. Jak to zrobić, to poradzę sobie sam, jednak np. jakaś aplikacja odczytuje kolejne pliki z katalogu, na podobnej zasadzie, jak większość programów do oglądania zdjęć, które wyświetlają kolejne zdjęcia z katalogu, a po ostatnim zdjęciu wyświetla pierwsze. Dla mnie, znacznie łatwiej jest przerobić aplikację z C# na C++ ze względu na dużo podobieństw (nieraz ręcznie przerabiałem z jednego na drugi między C++, Java i C#) niż z C# bądź C++ na JavaScript, więc cały ten WASM bardzo ułatwia sprawę.

Poza jakimiś specjalistycznymi przypadkami, moim zdaniem apka w przeglądarce ma znaczną przewagę nad taką samą apką desktopową, ale jedną z wad są właśnie bardzo ograniczone możliwości kontaktu z systemem operacyjnym, choć to się powoli zmienia. Np. da się nagrać i odtworzyć dźwięk, da się nagrać obraz z kamery, da się zrobić print screen, da się odczytać lokalizację urządzenia.

Temat założyłem nie dlatego, że nie wiem, jak ten problem rozwiązać, bo ja go już rozwiązałem samodzielnie (nawet w pierwszej wiadomości opisałem, jakie są znane mi sposoby), tylko założyłem dlatego, że mam przeczucie, iż niejeden programista przede mną chciał zbudować jakąś aplikację webową i miał taki sam problem, a ja "wynajduję koło na nowo" bez potrzeby. Tworzę sam jakiś serwer albo tworzę sam jakąś apkę z osadzoną przeglądarką, a możliwe jest że to jest już dawno zrobione, tylko ja nie wiem o istnieniu, ani jak się nazywa.

Patrzyłem co to jest Electron i Tauri (zresztą wymieniłem linki do tego, co sam znalazłem) i biorę pod uwagę to rozwiązanie, jednak w tym idea jest inna, choć można czerpać inspiracje, jakie zrobić API w JavaScript. Nie jest to ani przeglądarka z dodatkowym API, ani serwer, do którego można się podłączyć, tylko jest to narzędzie, które kompiluje apkę webową do "exe" z pozbawieniem ograniczeń działania w przeglądarce. Póki co, wydaje się, że najprostszą i najlepsza opcją jest dla mnie własny serwer WebSocket, który będzie takim pośrednikiem i w taką wchodzę. Zmajstrowałem taki serwer w C# (wykorzystując trzy takie programy z internetu, bo w jednym coś nie działa, w drugim czegoś brakuje itp.) i w Java, który nie ma żadnego GUI i to działa. Mam dwa komputery, na jednym Windows, na drugim Linux i na obu komputerach działa jeden i ten sam program serwera, bez przekompilowywania pod konkretny system operacyjny.

Myślę, że w razie czego, jak będę chciał mieć taki serwer na telefonie, to nie będzie trudne to przerobić, bo na Android też programuje się w Java, gorzej z iOS, nie znam ani Objective-C, ani Swift, ale na 99% nigdy nie będę posiadać iPhona ani tego potrzebować na iOS.

0
andrzejlisek napisał(a):

Dziękuję za propozycję, nie wiedziałem nic o gRPC, sprawdzę to. Na pierwszy rzut oka to chyba to samo co WebSocket lub HTTP, tylko inny protokół.

tak, grpc używa HTTP/2 które w zasadzie czyni websockety przestarzałymi i generuje automatycznie kod klienta / serwera tak że wywołania rpc są łatwe i przenośne. Napisanie własnego serwera websocket to trochę roboty a zwykły serwis np REST na HTTP nie daje tylu możliwości, np push czy streamowania i jest wolniejsze. gRPC to tak naprawdę tylko standard na komunikację między aplikacjami od googla, następca WCF w świecie .NET

andrzejlisek napisał(a):

Temat założyłem nie dlatego, że nie wiem, jak ten problem rozwiązać, bo ja go już rozwiązałem samodzielnie (nawet w pierwszej wiadomości opisałem, jakie są znane mi sposoby), tylko założyłem dlatego, że mam przeczucie, iż niejeden programista przede mną chciał zbudować jakąś aplikację webową i miał taki sam problem, a ja "wynajduję koło na nowo" bez potrzeby. Tworzę sam jakiś serwer albo tworzę sam jakąś apkę z osadzoną przeglądarką, a możliwe jest że to jest już dawno zrobione, tylko ja nie wiem o istnieniu, ani jak się nazywa.

No tak, rozwiązania takie jak electronjs, zobacz sobie visual studio code - jeśli to ma być javascript w aplikacji klienckiej to istnieje wiele rozwiązań, jeśli chcesz zrobić zwykłą stronę w przeglądarce to nie ma żadnego api do swobodnych operacji na dysku, musisz sam wskazać które pliki udostępniasz stronce i gdzie chcesz zapisać dane ze strony ewentualnie użyć sandboksowego systemu plików. Z perspektywy usera to dużo bezpieczniejsze i można dzięki temu bezpiecznie testować aplikacje w internecie, nie chciałbym żeby powstało API które to zmieni. To raczej już przeszłość. WASM może tyle co javascript.

1 użytkowników online, w tym zalogowanych: 0, gości: 1