Отображение изображения в Flutter, отправка через соединение с сокетами

#sockets #dart #flutter #base64 #ktor

#сокеты #dart #flutter #base64 #ktor

Вопрос:

Я пытаюсь создать соединение сервер-клиент с использованием сокетов. Сервер — это просто эхо-сервер. Я хочу отправлять туда разные типы данных. Я начал с изображений. Чего я хочу добиться, так это :

  1. Проанализируйте изображение, хранящееся в папке assets, для соответствующего типа данных
  2. Отправьте его на сервер Echo
  3. Получение данных обратно на мобильный (клиентский) сайт
  4. Отображение изображения, отправляемого таким образом (чтобы убедиться, что данные были отправлены правильно)

Я реализовал как клиент, так и сервер. Клиент находится в Flutter, сервер в Ktor. Реализация сервера была скопирована из руководства:https://ktor.io/servers/raw-sockets.html . Что я вижу, так это то, что мой сервер получает изображение и отправляет его обратно правильно, но я не могу его показать.

Серверный код :

 fun main() {
    runBlocking {
        val server = aSocket(ActorSelectorManager(Dispatchers.IO)).tcp().bind(InetSocketAddress("localhost", 8080))
        println("Started echo telnet server at ${server.localAddress}")

        while (true) {
            val socket = server.accept()

            launch {
                println("Socket accepted: ${socket.remoteAddress}")

                val input = socket.openReadChannel()
                val output = socket.openWriteChannel(autoFlush = true)

                try {
                    while (true) {
                        val line = input.readUTF8Line()

                        line?.let {
                            println("Client sent: $line")
                            output.writeStringUtf8(it)
                        }
                    }
                } catch (e: Throwable) {
                    println("Closing socket")
                    e.printStackTrace()
                    socket.close()
                }
            }
        }
    }
}
  

и клиент:

 class MyHomePage extends StatefulWidget {
  final String title;

  MyHomePage({Key key, @required this.title}) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Socket _socket;
  List<int> _connectionTimes = [];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
          child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: <Widget>[
          RaisedButton(
            child: const Text('Connect to socket'),
            color: Theme.of(context).accentColor,
            elevation: 4.0,
            onPressed: () {
              closeSocket();
              _connectToSocket().then((createdSocket) {
                setState(() {
                  _socket = createdSocket;
                });
              });
            },
          ),
          RaisedButton(
            child: const Text('Send to socket'),
            color: Theme.of(context).accentColor,
            elevation: 4.0,
            onPressed: () {
              _sendMessage();
            },
          ),
          StreamBuilder(
            stream: _socket,
            builder: (context, snapshot) {
              if(snapshot.hasData) {
                final bytes = base64Decode(utf8.decode(snapshot.data));
                return Image.memory(bytes);
              } else {
                return Text("no image");
              }
            },
          ),
        ],
      )),
    );
  }

  Future<Socket> _connectToSocket() async {
    final stopwatch = Stopwatch()..start();
    Socket sock = await Socket.connect('10.0.2.2', 8080);
    print("Connection time was ${stopwatch.elapsedMilliseconds}");
    return sock;
  }

  void _sendMessage() async{
    final imageBytes = await rootBundle.load('assets/images/dog.jpeg');
    final bytesAsString = base64Encode(imageBytes.buffer.asUint8List(imageBytes.offsetInBytes, imageBytes.lengthInBytes));
    print(bytesAsString);
    _socket.write(bytesAsString "n");
  }

  void closeSocket() {
    if (_socket != null) {
      _socket.close();
    }
  }

  @override
  void dispose() {
    _socket.close();
    super.dispose();
  }
}
  

Ошибка, которую я получаю, это :

 E/flutter ( 8235): [ERROR:flutter/lib/ui/painting/codec.cc(97)] Failed decoding image. Data is either invalid, or it is encoded using an unsupported format.
I/flutter ( 8235): ══╡ EXCEPTION CAUGHT BY IMAGE RESOURCE SERVICE ╞════════════════════════════════════════════════════
I/flutter ( 8235): The following _Exception was thrown resolving an image codec:
I/flutter ( 8235): Exception: operation failed
I/flutter ( 8235): ════════════════════════════════════════════════════════════════════════════════════════════════════
  

И, кроме того, у меня есть еще несколько вопросов:

  1. Есть ли лучший способ проанализировать изображение из ресурсов во Flutter?

  2. Есть ли способ отправить эти данные без добавления n в конце данных изображения?

  3. Возможно ли, что изображение будет слишком большим, и я не смогу отправить его в одном запросе? Если да, что мне следует изменить в коде, чтобы это заработало? Разделить его на несколько вызовов и использовать буфер как на клиенте, так и на сервере?

  4. Что я должен изменить в коде сервера, что позволило бы мне запускать его один раз, а также подключаться и отключаться от одного клиента несколько раз? (Раздражает, что при каждом изменении, которое я вношу в код Flutter, мне приходится перезапускать сервер, чтобы он работал правильно?

Я открыт для изменения реализации сервера на другой фреймворк / язык. Я хочу использовать Flutter, но это не обязательно должен быть Ktor на сайте сервера. Просто хотел проверить это в действии.

Комментарии:

1. Я не знаю ответов на ваши вопросы, но я бы начал с регистрации данных, которые вы отправляете, получения их обратно и сравнения (после кодирования base64 при отправке и перед декодированием base64 при получении — вероятно, с минимально возможным изображением, чтобы сделать строки короче для тестирования).

2. Итак, я попробовал действительно маленькое изображение (64×64 пикселей), и мой код работает. Я также добавил проверку, которая сравнивает байты (в виде строки), которые были отправлены на сервер, и которые были получены от него. Для маленького изображения это правильно, изображение отображается. На увеличенном изображении (225×225) выдается ошибка, написанная выше, и строка (байты) Я получил с сервера совершенно другое.

3. Я сравнил с большим изображением строку, полученную на сервере. Это точно то же самое, что и при отправке с клиента. Но отправка его обратно клиенту решает что-то совершенно другое.

Ответ №1:

Я могу использовать следующий код для чтения изображения в формате jpg из фотогалереи, отправки в node.js сервер с помощью socket.io, затем передача другому клиенту (при этом сохраните изображение в mysql, точно так же, как WhatsApp отправляет изображение)

 String base64Image1 = '';

// strImage1 is the path of the photo gallery retrieved by the plugin path_provider, plus the file name of the image.
String strImage1 = gv.strHomeImageFileWithPath   '_01.jpg';

var filImage1 = new File(strImage1);

List<int> imageBytes1 = filImage1.readAsBytesSync();

// use the following line if another client wants to display this image in html
// base64Image1 = 'data:image/jpg;base64,'   base64Encode(imageBytes1);

// Or, use the following line if another client wants to display this image in flutter
base64Image1 = base64Encode(imageBytes1);

// Send the b64 image string to the server
gv.socket.emit('PIBRequestPhotoClassify', [base64Image1]);

  

Кажется, что мои коды для декодирования и отображения строки изображения b64 такие же, как у вас, но коды для кодирования изображения отличаются, пожалуйста. попробуйте.

Комментарии:

1. Если я поместил файл под assets/images/dog.jpeg что я должен ввести как filename of an image ? Я пытался делать: final appPath = await getApplicationDocumentsDirectory(); String strImage1 = appPath.path '/assets/images/dog_small.jpeg'; И это приводит к ошибке: [ERROR:flutter/lib/ui/ui_dart_state.cc(148)] Unhandled Exception: FileSystemException: Cannot open file, path = '/data/user/0/com.example.flutterclient/app_flutter/assets/images/dog_small.jpeg'