【问题标题】:How to send a base64 image from client to server with react-native and socket.io?如何使用 react-native 和 socket.io 将 base64 图像从客户端发送到服务器?
【发布时间】:2021-11-11 22:41:51
【问题描述】:

在一个 react-native 项目中,我尝试使用 websockets (socket.io-client/socket.io) 将 base64 编码的字符串从客户端传输到服务器,然后再传输到另一个客户端。

我期待什么:

  • base64 图像字符串被发送到服务器并且服务器记录“[Data Received]”
  • 服务器随后发出“server_send_data”,接收客户端侦听器接收 base64 编码字符串

我正在经历什么:

  • base64 编码的图片字符串编码成功
  • 发送客户端成功发出事件
  • 服务器然后记录“[客户端断开连接] ...”
  • 接收客户端未收到任何事件

Server.js io/socket相关代码:

io.on("connection", socket => {
console.log('\n[ Connected To Client ]\n', socket.id)

  initRoom(socket)

  handleReceivedData(socket)

  socket.on('disconnect', () => {
    console.log('\n[ Client Disconnected ]\n', connectedAccounts)
    delete connectedAccounts
  })

  socket.on('client_send_data', e => {
    console.log(socket.id, 'sent', e)

    socket.to(e.account_id).emit('server_send_data', e.data)
  })
});


const initRoom = socket => {
  socket.emit('server_join_room')

  socket.on('client_join_room', accountId => {
    if (connectedAccounts.hasOwnProperty(accountId)) {
      const socketIds = connectedAccounts[accountId]['socket_ids']
      socketIds.push(socket.id)
    } else {
      connectedAccounts[accountId] = {
        inProgress: true,
        socket_ids: [socket.id]
      }
    }

    socket.join(accountId)
  })
}

const handleReceivedData = socket => {
  socket.on('admin_data_send', e => {
    console.log('[ Data Received ]')
    const { account_id, data } = e

    io.to(account_id).emit('display_data_send', data)
  })
}

照片选择器(发件人):

export default Photo = ({ navigation }) => {
  const [photoURI, onPhotoURI] = useState('')

  const openPicker = () => {
    ImagePicker.openCamera({
      cropping: false,
      includeBase64: true
    })
      .then(async image => {
        onPhotoURI(image.data)
        console.log('[ photo ]:', image)
        await AsyncStorage.removeItem('some_key')
        await AsyncStorage.setItem('some_key', String(image.data))
      })
      .catch(e => alert(e))
  }

  return (
    <SafeAreaView style={{ flex: 1, backgroundColor: 'gray' }}>
      <Text>Photo Data Stored: {photoURI !== '' ? 'True' : 'False'}</Text>
      <Pressable onPress={openPicker}>
        <Text>Take Picture</Text>
      </Pressable>
    </SafeAreaView>
  )
}
sendPic = async () => {
  console.log('[ Attempting to send Data to server ]')
  this.test.emit('admin_data_send', {
    account_id: ACCOUNT_ID,
    data: this.state.photoURI
  })
}

首页(客户端)

class Home extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      socketId: '',
      photoBase64: '',
      readyForSync: false,
      displayData: null
    }
    this.test = null
  }

  componentDidMount() {
    this.initSocket()
  }

  async componentDidUpdate() {
    try {
      const stored = await AsyncStorage.getItem('some_key')
      this.setState({ photoBase64: stored })
    } catch (e) {
      alert(e)
    }
  }

  initSocket = async () => {
    const endpoint = await isEmulator() ? emulator_socket : socket
    this.test = io(endpoint)
    
    this.test.on("connect", async () => {
      console.log(`${await isEmulator() ? '[ Emulator Connected ]' : '[ Physical Connected ]'}`, 'Endpoint:', endpoint, 'Id:', this.test.id)
      this.setState({ socketId: this.test.id })
    });

    this.test.on("disconnect", () => {
      console.log('[ disconnected ]', this.test.id); // undefined
    });

    this.test.on('server_join_room', () => {
      this.test.emit('client_join_room', ACCOUNT_ID)
    })

    this.test.on('display_data_send', e => {
      console.log('[ Display Data Recevied From Server ]')
      this.setState({ displayData: e })
    })
  }

  sync = async () => {
    this.setState({ readyForSync: true })
    console.log('[ Sync Started ]')

    this.test.emit('display_ready', {account_id: ACCOUNT_ID})
  }

  sendPic = async () => {
    const endpoint = await isEmulator() ? emulator_socket : socket
    console.log('[ Attempting to send Data to server ]')

    // divide base64 into 3 segments
    console.log('[ base64 length ]:', this.state.photoURI.length)
    // loop the emit
    this.test.emit('admin_data_send', {
      account_id: ACCOUNT_ID,
      data: this.state.photoBase64
    })
  }

  render() {
    return (
      <SafeAreaView style={{ flex: 1, backgroundColor: '#d1d1d1' }}>
        {this.state.readyForSync ? (
          <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
            <ActivityIndicator animating={true} color="#333" size="large" />
            <Text>Awaiting Data From Admin Device...</Text>
            {
              this.state.displayData !== null && (
                <View style={{ width: 200, height: 200 }}>
                  <Image src={{ uri: `data:image/jpeg;base64,${this.state.displayData}` }} />
                </View>
              )
            }
          </View>
        ) : (
          <>
            <Text style={{ textAlign: 'center', fontSize: 20, fontWeight: 'bold', marginTop: 20 }}>{Platform.isTV ? 'TV App' : Platform.OS}</Text>

            <Text style={{ textAlign: 'center', fontSize: 20, fontWeight: 'bold', marginTop: 20 }}>Socket ID: {this.state.socketId}</Text>

            <Pressable style={styles.press} onPress={this.sync}>
              <Text> SYNC APP </Text>
            </Pressable>

            <Pressable style={styles.press} onPress={() => this.props.navigation.navigate('Photo')}>
              <Text> TAKE PIC </Text>
            </Pressable>

            <Pressable style={styles.press} onPress={this.sendPic}>
              <Text> SEND PIC </Text>
              <Text>{this.state.photoBase64}</Text>
            </Pressable>
          </>
        )}
      </SafeAreaView>
    )
  }
}

如何确保连接没有丢失,数据被服务器接收并成功传输回接收客户端?

【问题讨论】:

  • 一个minimal reproducible example,请。没有关于如何使用您的客户的代码。
  • @x00 为你更新
  • 可能有很多不同的原因。现在我们不确定这是客户端还是服务器的问题。为确保它在客户端(移动应用程序)上,请尝试先使用工具将大小图像转换为 base64(base64-image.de),然后使用 Postman 之类的工具将其发送到服务器。如果成功,那么只需增加你的超时时间socket.heartbeatTimeout = 30000;,否则它是服务器的问题

标签: reactjs react-native websocket socket.io


【解决方案1】:

我怀疑maxHttpBufferSize,因为默认值只有 1 MB (1e6)。您的 base64 编码图像可能会导致消息超出此限制,根据文档,这将关闭套接字。

io 的初始化中,指定可能会有所帮助

const io = new Server(httpServer, {
  maxHttpBufferSize: 1e8 // 100 MB
});

【讨论】:

    【解决方案2】:

    请尝试将字符串作为数据包发送并在您的前端重新收集它们。这样,您也可以发送大文件而不会出现数据丢失的问题,而且速度和性能都非常高效。尝试使用并行处理发送这些数据包。

    如果您使用的是节点,请尝试使用 lodash 函数或 bluebird 函数来实现您想要的。这也将大大提高文件发送/接收性能。

    还可以使用atob() 将其转换回图像。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-26
      • 1970-01-01
      • 1970-01-01
      • 2015-03-25
      • 1970-01-01
      相关资源
      最近更新 更多