You have the following questions about this thing. Why don’t you use QQ, wechat, SCP, SSH, Photoshop, CAD, Android Studio, VS Code, Xcode, netease Cloud, King of Glory, Tiantian Cool Run?

The back key is in the top left corner!

Feature list

  • File deployment similar to Nginx and Tomcat.
  • File sharing on a LAN like chat, point-to-point connection, no server transfer.
  • Support image and video message preview directly (video preview only supports Android and Web)
  • Multiple devices can be shared and viewed simultaneously
  • Download progress display, network speed display
  • The browser quickly joins the share
  • LAN device discovery function
  • Historical Message Retrieval

There are always various problems in the early stage of software, rational feedback, I will fix it in my spare time.

Since this is an open source project, you can also submit an issue to let me know about problems in the software.

Some development experience

I often encounter such a situation, I want to download some large files on the PC to the mobile phone, or I want to handle the large files on the phone to the computer, such as brush bag, movie, so open the QQ or wechat file assistant, click upload, 100KB /s upload speed, instant unhappy 😩.

When I joined the company, such scenes became more frequent. At this time, I wanted to transfer files through MIUI+. As far as I am concerned, 90% of the systems I used in college were macOS(Black Apple), so MIUI+ is no longer useful, and MIUI+ does not support hot PC connection on mobile phones. In the company is also macOS, using QQ, wechat, the same will pass through the server, too slow, Windows VERSION of QQ LAN to send files to PC seems to be a few times to go through the LAN.

And have the above requirements, the general mobile phone and computer is in a LAN, such as me, is often a mobile phone hotspot with the computer.

So IN my spare time, I modified the app “Quick Enjoy”, which only supports file deployment before, simply speaking, file deployment similar to Nginx or Tomcat. When I first developed this app, I did not often need this function, but I heard about this function. Will simplify the need for Kindle users to transfer e-books.

Method of use

The first device to turn on the Instant feed will need to create a shared window, similar to the group chat feature.

  1. If the LOCAL area network is not complex, there is no subnets, each end of the speed enjoy start, one end to create a room, the other end can directly receive a pop-up prompt.
  2. If there is no popup window, Android can join a shared room by scanning the QR code displayed after the room is created, or by using the URL prompted after the room is created.
  3. If only one end of the client is installed, you can still join the shared room by opening the URL from the browser, but you can only download files, not send them.

After more tests, generally mobile phone hot spot, computer connection, or mobile phone computer connection with the same router, create a room can receive a pop-up window. In a complex LAN, for example, if a router needs to connect more than 255, it will divide the network into subnets. In this case, it cannot broadcast the behavior of the device to create a room through UDP broadcast.

screenshots

There are only two pages in the entire app.








Implementation of file selection

I originally really wanted to use file_picker on pub, but the file manager selects a file, copies the file (regardless of size) to the data directory, and returns the copied path to the data directory.

Here’s an example: When file_picker is used to select a 3g file, the whole screen will return temporarily, but the code is stuck in the “await pick()” line. Depending on the speed of the device, it may be several minutes until it copies the file to the data directory and we get some information about the file. The path is /data/data/package_name/cache/ XXX. I guess it is designed to be compatible with higher Versions of Android, but I don’t think it is a good solution.

It could be my fault, but if you find that file_picker can get the real path to the file, please point it out in the comments section.

Finally use their own a file manager, choose pictures and video is not very friendly, to be optimized later.

Realization of LAN device discovery function

When amway multicast_dns is multicast_dns, it does not have a readme at all. When Amway multicast_dns is multicast_DNS, it does not have a readme at all. When Amway multicast_dns is multicast_DNS, it does not have a readme.

Android seems to need it

WifiManager wifi;
Context context = getApplicationContext();
wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
mLock = wifi.createMulticastLock("discovery-multicast-lock");
mLock.setReferenceCounted(true);
mLock.acquire();
Copy the code

And add the corresponding permission to open the multicast message receiving, because android default enabled multicast message receiving will increase power consumption, but AFTER I test, found that it is not necessary.

The final code is as follows:

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

InternetAddress _mDnsAddressIPv4 = InternetAddress('224.0.0.251');
const int _port = 4545;
typedef MessageCall = void Function(String data);

bool _hasMatch(String value, String pattern) {
  return (value == null)?false : RegExp(pattern).hasMatch(value);
}

/// Copy of getx
extension IpString on String {
  bool get isIPv4 =>
      _hasMatch(this.r'^(? : (? : ^ | \.) (? : 2 (? :5[0-5]|[0-4]\d)|1? \d? \d)){4}$');
}

/// Devices can be discovered on the LAN through multicast and broadcast
class Multicast {
  final int port;
  List<MessageCall> _callback = [];
  RawDatagramSocket _socket;
  bool _isStartSend = false;
  bool _isStartReceive = false;

  Multicast({this.port = _port});

  /// Stop sending messages to udp
  void stopSendBoardCast() {
    if(! _isStartSend) {return;
    }
    _isStartSend = false;
    _socket.close();
  }

  /// Receives UDP broadcast messages
  Future<void> _receiveBoardCast() async {
    RawDatagramSocket.bind(
      InternetAddress.anyIPv4,
      port,
      reuseAddress: true,
      reusePort: false,
      ttl: 255,
    ).then((RawDatagramSocket socket) {
      // Receive multicast messages
      socket.joinMulticast(_mDnsAddressIPv4);
      // Enable broadcast support
      socket.broadcastEnabled = true;
      socket.readEventsEnabled = true;
      socket.listen((RawSocketEvent rawSocketEvent) async {
        final Datagram datagram = socket.receive();
        if (datagram == null) {
          return;
        }
        String message = utf8.decode(datagram.data);
        _notifiAll(message);
      });
    });
  }

  void _notifiAll(String data) {
    for (MessageCall call in _callback) {
      call(data);
    }
  }

  Future<void> startSendBoardCast(String data, {Duration duration}) async {
    if (_isStartSend) {
      return;
    }
    _isStartSend = true;
    _socket = await RawDatagramSocket.bind(
      InternetAddress.anyIPv4,
      0,
      ttl: 255,); _socket.broadcastEnabled =true;
    _socket.readEventsEnabled = true;
    while (true) {
      _boardcast(data);
      if(! _isStartSend) {break;
      }
      await Future.delayed(duration ?? Duration(seconds: 1));
    }
  }

  Future<void> _boardcast(String msg) async {
    List<int> dataList = utf8.encode(msg);
    _socket.send(dataList, _mDnsAddressIPv4, port);
    await Future.delayed(const Duration(milliseconds: 10));
    final List<String> address = await _localAddress();
    for (final String addr in address) {
      final tmp = addr.split('. ');
      tmp.removeLast();
      final String addrPrfix = tmp.join('. ');
      final InternetAddress address = InternetAddress(
        '$addrPrfix255 '\.,); _socket.send( dataList, address, port, ); } } Future<List<String>> _localAddress() async {
    List<String> address = [];
    final List<NetworkInterface> interfaces = await NetworkInterface.list(
      includeLoopback: false,
      type: InternetAddressType.IPv4,
    );
    for (final NetworkInterface netInterface in interfaces) {
      // Iterate over the nic
      for (final InternetAddress netAddress in netInterface.addresses) {
        // Traverses the IP address of the nic
        if(netAddress.address.isIPv4) { address.add(netAddress.address); }}}return address;
  }

  void addListener(MessageCall listener) {
    if(! _isStartReceive) { _receiveBoardCast(); _isStartReceive =true;
    }
    _callback.add(listener);
  }

  void removeListener(MessageCall listener) {
    if(_callback.contains(listener)) { _callback.remove(listener); }}}Copy the code

Send a message

multicast.startSendBoardCast('hello');
Copy the code

Listen to the message

Multicast multicast = Multicast();
multicast.addListener((data) {
  
});
Copy the code

Register to listen and receive messages from the LAN.

Let’s see what happens

It’s still faster to be close to the router.

The relevant data

  • UDP multicasting working properly in Dart(Linux) but not in Flutter(Android Emulator)
  • Implement Android LAN Peer Link via UDP Broadcast
  • Receiving UDP Multicast Not Working
  • Android device not receiving multicast package

The realization of chat server

The idea was to listen for connections to sockets and push the connected sockets onto a stack. When a socket sends a message, the server traverses the stack and forwards the message.

But because of the need to support the physical platform and the Web, so we can not directly use the native socket to implement the chat server, so the Web will not be able to connect, so we consider using WebSocket, here is the pit. The WebSocket on the physical device comes from DART: IO and the WebSocket on the Web platform comes from DART: HTML. Therefore, it is considered to abstract the common implementation of the two Websockets and execute the corresponding platform code through the runtime package guide.

Getx Websocket does this for me, so I pulled it down and used it.

  getsocket: ^ 1.0.0
Copy the code

The service side hole

The server is also integrated into the client app and is written using some of the Dart libraries.

There is a pit in the server side code that I have not solved for a long time. For the server side code implementation of file sharing, I tried to use two libraries, one is http_server and the other is shelf_static. The former shares the video of the file, which can be quickly buffed and played, but the other side that shares the video, Running memory consumption is basically proportional to the size of the video file. In other words, if the video is 3GB, running memory consumption will be close to 3GB as the player continues buffering. This is not acceptable, as mentioned in the http_server issue, but it is still an unsolved situation. And the warehouse has been officially set up as Archived.

So NOW I have changed shelt_static, but the video of this package deployment cannot be buffed and played in real time. It seems that some back-end knowledge is designed in this part, but I failed to solve this problem. Only after the preview end is completely downloaded, can I play 🤤.

2021/06/11 has been resolved and has been fed back to the official DART warehouse shelf_static/issues/58.

The Windows of the pit

Since there is no root path on Windows, any path starts with a C:\ D:\\ format. The trick is how to create an all-file deployment with Dart, similar to Tomcat, and how to access all files!

The following implementations are considered:

Iterate over the drive letter, deploy each disk, and setC:\\Converted to/c/In this form, the visitor then visits the belt/cTo indicate the drive letter to be accessed

But the implementation was not written with DART in the end.

The web hole

Platform to judge

We need to use code like platform. isAndroid, which will give an error if it runs on the Web because dart: IO is not available on the Web, so we’ll consider wrapping another if(! KIsWeb), and found a more elegant implementation of get_utils in the getX repository, see the source code.

The code of the current judging platform:

  if (GetPlatform.isAndroid) 
Copy the code

This is a case where GET guides the packet at runtime to avoid a Web access exception.

The URL to get

Dart: HTML, dart: HTML, dart: HTML, dart: HTML, dart: HTML, dart: HTML, dart: HTML

document.dart

library document;

export 'document_io.dart' if (dart.library.html) 'document_broswer.dart';
Copy the code

document_io.dart

String url = ' ';
Copy the code

document_broswer.dart

import 'dart:html';

String url = window.document.baseUri;
Copy the code

The Flutter Web cannot be opened on mobile devices other than Chrome

After building the web of flutter, I found that only Chrome could open this web page. Besides, I didn’t know how to debug the mobile web at first. After opening the flutter, the screen went blank and I searched a bunch of issues but found no result, github.com/flutter/flu… I decided to try something else.

By changing index.html the code is as follows:

<script src="https://cdn.bootcss.com/vConsole/3.2.2/vconsole.min.js"></script>
<script>  var vConsole = new VConsole();  </script>
Copy the code

Then open the phone again and a green debugging window will appear, similar to wechat mini program. Click and you can see the log.

Finally found the error line:

waitForActivation(reg.installing ?? reg.waiting);
Copy the code

That?? An error

It is a new syntax, es6 feature, etc. The entire line is deleted, leaving only one line loadMainDartJs() for dart. “, and then the various browsers on the mobile end are opened.

Open source address

speed_share

Feel helpful to a ⭐️ bar ~

Experience the address

Cool Ann address

Github download address

Currently only compiled for MAC and Windows

Android download files at /sdcard/SpeedShare

enjoy ~ ~ ~