flutter Dart:HttpServer无法正确响应iOS Webview和iOS浏览器上的.mp4文件

enxuqcxy  于 2023-05-29  发布在  Flutter
关注(0)|答案(1)|浏览(237)
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:path_provider/path_provider.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Appname',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Sunrion'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Future<String> initialize() async {
    await _getServer();

    if (kDebugMode) {
      debugPrint('appDocumentsDirectory : ${appDocumentsDirectory.path}');
    }

    final preferences = await SharedPreferences.getInstance();

    final isExtracted = preferences.getBool('isExtracted');

    if (isExtracted != true) {
      const platform = MethodChannel('flutter.native/zipchannel');
      try {
        var pathToAssetsZip = 'assets/resources/app.zip';

        await platform.invokeMethod('extractAssetsZip', {
          'name': pathToAssetsZip,
          'path': '${appDocumentsDirectory.path}/'
        });

        preferences.setBool('isExtracted', true);
      } catch (_) {}
    }

    return appDocumentsDirectory.path;
  }

  HttpServer? server;

  startServer() async {
    await for (var request in server!) {
      final path = request.uri.path;

      var filePath = '${appDocumentsDirectory.path}/app$path';

      final file = File(filePath);

      var existsString = '';

      if (path.endsWith('.mp4')) {
        final data = await file.readAsBytes();

        request.response
          ..headers.contentType = ContentType('video', 'mp4')
          ..add(data)
          ..close();

      } else if (path.endsWith('.png')) {
        final exists = file.existsSync();

        existsString = ' :: $exists';

        request.response
          ..add(await file.readAsBytes())
          ..close();
      } else if (path.endsWith('.js') || path.endsWith('.css')) {
        final fileContent = await file.readAsString();

        final exists = file.existsSync();

        existsString = ' :: $exists';

        final mime = path.endsWith('.js') ? 'javascript' : 'css';

        request.response
          ..headers.contentType = ContentType('text', mime, charset: 'utf-8')
          ..write(fileContent)
          ..close();
      } else if (path == '/') {
        filePath = '${appDocumentsDirectory.path}/app/index.html';

        final file = File(filePath);

        final fileContent = await file.readAsString();

        request.response
          ..headers.contentType = ContentType("text", "html", charset: "utf-8")
          ..write(fileContent)
          ..close();
      } else {
        request.response
          ..headers.contentType = ContentType("text", "plain", charset: "utf-8")
          ..write('Unknown path')
          ..close();
      }

      debugPrint('requestpath : $path$existsString');
    }
  }

  late Directory appDocumentsDirectory;

  Future<HttpServer> _getServer() async {
    if (server == null) {
      appDocumentsDirectory = await getApplicationDocumentsDirectory();

      server = await HttpServer.bind(InternetAddress.loopbackIPv4, 8080);

      startServer();
    }

    if (kDebugMode) {
      print(
          "Server running on IP : ${server!.address} On Port : ${server!.port}");
    }
    return server!;
  }

  late WebViewController webViewController;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: FutureBuilder(
          future: initialize(),
          builder: (context, snapshot) {
            if (snapshot.connectionState != ConnectionState.done) {
              return Container();
            }

            final address = server!.address.address;

            var htmlPath = 'http://$address:${server!.port}';

            return Column(
              children: [
                Expanded(
                  child: WebView(
                    backgroundColor: Colors.white,
                    javascriptMode: JavascriptMode.unrestricted,
                    onWebViewCreated: (controller) async {
                      webViewController = controller;
                      await controller.loadUrl(htmlPath);
                    },
                    initialMediaPlaybackPolicy:
                        AutoMediaPlaybackPolicy.always_allow,
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.all(8),
                  child: TextButton(
                    onPressed: () {
                      webViewController.loadUrl(htmlPath);
                    },
                    child: const Text('Hit me'),
                  ),
                )
              ],
            );
          }),
    );
  }
}

我试图加载SPA在WebView从reactjs这是放置在应用程序DocumentsDirectory。
Webview正在加载HTML页面,但未加载其中的视频文件。有一个字节范围的概念,我试图在HTTPserver中实现,但未能实现。
我可能在上面的MP4 IF条件中犯了一个错误。如果有人能帮忙的话我会很感激的。

jjjwad0x

jjjwad0x1#

我使用了shelf库(https://pub.dev/packages/shelf),它有助于管理对分块数据的响应。

import 'dart:convert';
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:shelf/shelf.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'package:path_provider/path_provider.dart';
import 'package:shelf/shelf_io.dart' as shelf_io;

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Appname',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Sunrion'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Future<String> initialize() async {
    await _getServer();

    if (kDebugMode) {
      debugPrint('appDocumentsDirectory : ${appDocumentsDirectory.path}');
    }

    final preferences = await SharedPreferences.getInstance();

    final isExtracted = preferences.getBool('isExtracted');

    if (isExtracted != true) {
      const platform = MethodChannel('flutter.native/zipchannel');
      try {
        var pathToAssetsZip = 'assets/resources/app.zip';

        await platform.invokeMethod('extractAssetsZip', {
          'name': pathToAssetsZip,
          'path': '${appDocumentsDirectory.path}/'
        });

        preferences.setBool('isExtracted', true);
      } catch (_) {}
    }

    return appDocumentsDirectory.path;
  }

  HttpServer? server;

  late Directory appDocumentsDirectory;

  Future<HttpServer> _getServer() async {
    if (server == null) {
      appDocumentsDirectory = await getApplicationDocumentsDirectory();

      var handler = const Pipeline()
          .addMiddleware(logRequests())
          .addHandler(_echoRequest);

      server = await shelf_io.serve(handler, InternetAddress.loopbackIPv4, 8080);

      server!.autoCompress = true;
    }

    if (kDebugMode) {
      print(
          "Server running on IP : ${server!.address} On Port : ${server!.port}");
    }
    return server!;
  }

  Future<Response> _echoRequest(Request request) async {
    final path = request.url.path;

    var filePath = '${appDocumentsDirectory.path}/app/$path';

    // final file = File(filePath);

    if (request.url.path == '') {
      filePath = '${appDocumentsDirectory.path}/app/index.html';

      final file = File(filePath);

      final fileContent = await file.readAsString();

      final headers = {"content-type": "text/html"};

      return Response.ok(fileContent,
          headers: headers, encoding: Encoding.getByName('utf-8'));
    } else if (path.endsWith('.js') || path.endsWith('.css')) {
      final file = File(filePath);

      final fileContent = await file.readAsString();

      final mime = path.endsWith('.js') ? 'javascript' : 'css';

      final headers = {"content-type": 'text/$mime'};

      return Response.ok(fileContent,
          headers: headers, encoding: Encoding.getByName('utf-8'));
    } else if (path.endsWith('.png')) {
      final file = File(filePath);

      final fileContent = await file.readAsBytes();

      return Response.ok(
        fileContent,
      );
    } else if (path.endsWith('.mp4')) {
      final file = File(filePath);

      final fileContent = await file.readAsBytes();

      return Response.ok(
        fileContent,
      );
    }

    return Response.ok('Request for "${request.url}"');
  }

  late WebViewController webViewController;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: FutureBuilder(
          future: initialize(),
          builder: (context, snapshot) {
            if (snapshot.connectionState != ConnectionState.done) {
              return Container();
            }

            final address = server!.address.address;

            var htmlPath = 'http://$address:${server!.port}';

            // var htmlPath = 'file://${appDocumentsDirectory.path}/app/playvideo.html';

            return Column(
              children: [
                Expanded(
                  child: WebView(
                    backgroundColor: Colors.white,
                    javascriptMode: JavascriptMode.unrestricted,
                    onWebViewCreated: (controller) async {
                      webViewController = controller;
                      await controller.loadUrl(htmlPath);
                    },
                    initialMediaPlaybackPolicy:
                        AutoMediaPlaybackPolicy.always_allow,
                  ),
                )
              ],
            );
          }),
    );
  }
}

相关问题