开天辟地 HarmonyOS(鸿蒙) - 网络: http(通过 rcp 实现 http 请求,上传下载和流)

源码 https://github.com/webabcd/HarmonyDemo
作者 webabcd

开天辟地 HarmonyOS(鸿蒙) - 网络: http(通过 rcp 实现 http 请求,上传下载和流)

示例如下:

pages\network\RcpDemo3.ets

/*
 * http 请求
 * 本例用于演示如何通过 rcp 实现 http 请求,上传下载和流
 * 相对于 Network Kit 的 HttpRequest 来说,通过 Remote Communication Kit 的 rcp 实现 http 请求会更简单且功能更强大
 *
 * 需要在 src/main/module.json5 中添加 ohos.permission.INTERNET 权限
 */

import {  TitleBar } from '../TitleBar';
import { rcp } from '@kit.RemoteCommunicationKit';
import { BusinessError } from '@kit.BasicServicesKit';

@Entry
@Component
struct RcpDemo3 {

  build() {
    Column() {
      TitleBar()
      Tabs() {
        TabContent() { MySample1() }.tabBar('下载文件').align(Alignment.Top)
        TabContent() { MySample2() }.tabBar('上传文件').align(Alignment.Top)
        TabContent() { MySample3() }.tabBar('流式上传').align(Alignment.Top)
      }
      .scrollable(true)
      .barMode(BarMode.Scrollable)
      .layoutWeight(1)
    }
  }
}

@Component
struct MySample1 {

  @State message: string = ""

  /*
   * rcp.createSession() - 创建一个 rcp.Session 对象
   *   downloadToStream() - 下载指定 url 的数据并保存到指定的流
   *   downloadToFile() - 下载指定 url 的数据并保存到指定的文件
   *     url - 需要下载的 url
   *     downloadTo - 需要保存到的文件(一个 DownloadToFile 对象)
   * DownloadToFile - 用于描述需要保存到的文件的信息
   *   kind - 设置为 'file' 则代表需要保存到指定的文件
   *   file - 需要保存到的文件的路径
   */
  async download_sample() {
    this.message = "开始下载文件\n"

    const savePath = getContext(this).filesDir + "/rcp_sample1.txt"
    let downloadToFile: rcp.DownloadToFile = {
      kind: 'file',
      file: savePath,
    }

    const session: rcp.Session = rcp.createSession()
    session.downloadToFile('http://192.168.8.197:8001/api?k1=v1&k2=v2', downloadToFile).then((response) => {
      this.message += `response code: ${response.statusCode}\n`
      this.message += `save path: ${savePath}\n`
    }).catch((err: BusinessError) => {
      this.message += `error: ${JSON.stringify(err)}\n`
    }).finally(() => {
      session.close()
    });
  }

  build() {
    Column({space:10}) {

      Button("下载文件").onClick(async () => {
        await this.download_sample()
      })

      Text(this.message)
    }
  }
}

@Component
struct MySample2 {

  @State message: string = ""

  /*
   * rcp.createSession() - 创建一个 rcp.Session 对象
   *   uploadFromStream() - 上传指定的流到指定的 url
   *   uploadFromFile() - 上传指定地址的文件到指定的 url
   *     url - 服务端 url
   *     uploadFrom - 需要上传的文件(一个 UploadFromFile 对象)
   * UploadFromFile - 用于描述需要上传的文件的信息
   *   filePath - 需要上传的文件的路径
   */
  async upload_sample() {
    this.message = "开始上传文件\n"

    let filePath = getContext(this).resourceDir + "/son.jpg"
    let uploadFromFile = new rcp.UploadFromFile(filePath)

    const session: rcp.Session = rcp.createSession({headers:{"filename":"son.jpg"}})
    session.uploadFromFile('http://192.168.8.197:8001/upload', uploadFromFile).then((response) => {
      this.message += `response json: ${JSON.stringify(response.toJSON())}\n`
    }).catch((err: BusinessError) => {
      this.message += `error: ${JSON.stringify(err)}\n`
    }).finally(() => {
      session.close()
    });
  }

  build() {
    Column({space:10}) {

      Button("上传文件").onClick(async () => {
        await this.upload_sample()
      })

      Text(this.message)
    }
  }
}

@Component
struct MySample3 {

  @State message: string = ""

  /*
   * rcp.createSession() - 创建一个 rcp.Session 对象
   *   post()
   *     url - 服务端 url
   *     networkInputQueue - 需要上传的数据队列(一个 NetworkInputQueue 对象)
   * NetworkInputQueue - 需要上传的数据队列
   *   write() - 向队列中写入数据,队列中的数据会按顺序被上传到服务端
   *   close() - 关闭队列,即断开连接
   */
  async upload_sample() {
    this.message = "开始流式上传\n"

    const networkInputQueue = new rcp.NetworkInputQueue();

    // 每隔 10 毫秒向队列中写入数据(队列中的数据会按顺序被上传到服务端),用于模拟流式上传数据
    let counter = 0;
    const interval = setInterval(() => {
      networkInputQueue.write(`${counter++} `);
      if (counter === 100) {
        clearInterval(interval);
        networkInputQueue.close();
      }
    }, 10);

    const session: rcp.Session = rcp.createSession({headers:{"filename":"rcp_sample3.txt"}})
    session.post('http://192.168.8.197:8001/upload', networkInputQueue).then((response) => {
      this.message += `response json: ${JSON.stringify(response.toJSON())}\n`
    }).catch((err: BusinessError) => {
      this.message += `error: ${JSON.stringify(err)}\n`
    }).finally(() => {
      session.close()
    });
  }

  build() {
    Column({space:10}) {

      Button("流式上传").onClick(async () => {
        await this.upload_sample()
      })

      Text(this.message)
    }
  }
}

\webapi\webapi\webserver.py

from aiohttp import web
import asyncio

def setup_routes(app):
    app.router.add_route('*', '/api', httpapi)
    app.router.add_route('*', '/redirect', httpapi_redirect)
    app.router.add_route('*', '/upload', httpapi_upload)

async def launch():
    app = web.Application()
    runner = web.AppRunner(app)
    setup_routes(app)
    await runner.setup()
    site = web.TCPSite(runner, '0.0.0.0', 8001)
    await site.start()


async def httpapi(request):
    k1 = request.query.get('k1', '')
    k2 = request.query.get('k2', '')
    
    h1 = request.headers.get('custom-header1', '')
        
    data = await request.content.read()
    postData = data.decode()
        
    result = f"method:{request.method}, k1:{k1}, k2:{k2}, h1:{h1}, postData:{postData}"

    await asyncio.sleep(3)
    return web.Response(text=result)


async def httpapi_redirect(request):
    return web.Response(status=302, headers={'Location': '/api?k1=v1&k2=v2'})


async def httpapi_upload(request):
    filepath =  request.headers.get('filename', 'unknown.unknown')

    with open(filepath, 'wb') as f:
        while True:
            chunk = await request.content.read(1024 * 1024) 
            if not chunk:
                break
            f.write(chunk)

    return web.json_response({'message': f'文件成功保存到 {filepath}'}, status=200)

源码 https://github.com/webabcd/HarmonyDemo
作者 webabcd

posted @ 2025-05-27 14:44  webabcd  阅读(21)  评论(0)    收藏  举报