鸿蒙Next(HarmonyOS) 通过HttpRequest上传文件到服务端(带参数)

1.项目介绍

在移动应用开发中,文件上传是一个常见的需求。今天,我们将一起学习鸿蒙Next如何将本地文件上传到服务器。这个过程虽然设计多个步骤,但只要我们逐步进行,就能够轻松实现。

2.流程概述

首先,我们来展示一下整个文件上传的流程:

步骤内容
1选择文件
2准备请求,设置请求头和请求体
3发送请求到服务端
4处理响应
5处理文件上传带参的情况

2.1 选择本地文件

这里我们写入内容到指定文件下,实际应用只需要替换文件路径即可。

import { common } from '@kit.AbilityKit';
import { fileIo } from '@kit.CoreFileKit';
import { systemDateTime } from '@kit.BasicServicesKit';

let context = getContext(this) as common.UIAbilityContext
let filesDir = context.filesDir;

let filePath = filesDir + '/test.txt' //文件路径

// 上传文件使用的分隔符
let boundary: string = '----Boundary';

// 写入文件
function writeFile(filePath: string){
  // 用文件路径,创建一个文件流。w+: 覆盖式读写,无论该文件存在与否
  let fileStream = fileIo.createStreamSync(filePath, "w+");
  const content = "这是一段测试文件。‌ 写入时间: " + systemDateTime.getTime().toString();
  console.log("文件内容 : ", content);
  fileStream.writeSync(content);
  fileStream.close();
}

2.2 准备请求,设置请求头和请求体

http post请求的格式是这样的

Content-type:multipart/formdata, boundary=boundary

--boundary
Content-disposition: form-data; name="name"

mike

--boundary
Content-disposition: form-data; name: "pic", filename: "myPic.jpg"
Content-type: image/jpg

<myPic.jpg>

--boundary--

这就是我们上传时需要配置的http
boundary规定的是什么,后面就要一样。boundary 是分割各个参数的分隔符。 注意:boundary结尾时,是–boundary–,代表请求体结束。multipart/formdata 上传的请求头必须包含一个特殊的信息:Content-type 其值也必须是multipart/formdata 如果是一个文件则需要文件信息filename:"myPic.jpg"与文件内容类型Content-type:image/jpg,<myPic.jpg> 则是我们想要上传的图片或者文件。
一定要严格按照规则,否则不会成功

import fs from '@ohos.file.fs';
import { util } from "@kit.ArkTS";

function getBodyData(): string{
  let fileName = "";
  if (filePath && filePath.includes("/")) {
    const fileNameArr = filePath.split("/");
    fileName = fileNameArr[fileNameArr.length-1];
  }
  //选择要上传的文件的内容
  let fileContent: string = buf2String(readContentFromFile(filePath))

  let bodyStr = "";
  bodyStr += "--" + boundary + "\r\n";
  bodyStr += "Content-Disposition: form-data; name=\"file\";filename=\"" + fileName + "\"\r\n";
  bodyStr += "\r\n";
  bodyStr += fileContent;
  bodyStr += "\r\n";
  bodyStr += "--" + boundary + "--\r\n";

  return bodyStr;
}

//从文件读取内容
function readContentFromFile(fileUri: string): ArrayBuffer {
  let buf = new ArrayBuffer(1024 * 4);
  let file = fs.openSync(fileUri, fs.OpenMode.READ_ONLY);
  let readLen = fs.readSync(file.fd, buf, { offset: 0 });
  let result = buf.slice(0, readLen)
  fs.closeSync(file);
  return result
}

//ArrayBuffer转utf8字符串
function buf2String(buf: ArrayBuffer) {
  let msgArray = new Uint8Array(buf);
  let textDecoder = util.TextDecoder.create("utf-8");
  return textDecoder.decodeWithStream(msgArray)
}

2.3 发送请求到服务端

设置Content-Type为multipart/form-data; boundary=xxx, xxx为上传文件使用的分隔符(可自定义)
bodyData为我们的请求额外参数;

import http from '@ohos.net.http';

type RequestListener = (isRequestSuc: boolean, requestData: string) => void;

async function requestPost(url: string, bodyData: string | undefined, httpResponseBlock: RequestListener){
  let contentTypeStr = "multipart/form-data; boundary=" + boundary;
  let postOptions: http.HttpRequestOptions = {
    method: http.RequestMethod.POST,
    extraData: bodyData,
    header: {
      'Content-Type': contentTypeStr
    },
    connectTimeout: 60000,
  };
  console.info("requestUrl --- " + url);
  const httpRequest = http.createHttp();
  httpRequest.request(url, postOptions, (err: Error, data: http.HttpResponse) => {
    if (!err) {
      console.info('Result:' + data.result);
      // console.info('code:' + data.responseCode);
      // console.info('type:' + JSON.stringify(data.resultType));
      // console.info('header:' + JSON.stringify(data.header));
      if (httpResponseBlock) httpResponseBlock(true, data.result as string);
    } else {
      console.error('error:' + JSON.stringify(err));
      if (httpResponseBlock) httpResponseBlock(false, JSON.stringify(err));
    }
  });
}

2.4 处理响应

文件上传时调用以下代码,处理请求响应。

// 代码调用
writeFile(filePath);
let url = "xxx";
let bodyData = getBodyData();
requestPost(url, bodyData, (isRequestSuc: boolean, requestData: string) => {
  if (isRequestSuc) {
    console.info('上传成功 --- ' + requestData);
  }else {
    console.error("请求失败 err --- " + requestData.toString());
  }
});

2.5 处理文件上传带参的情况

拼接带参参数时每个参数都需要拼接分隔符格式。

function getBodyData(): string{
  let fileName = "";
  if (filePath && filePath.includes("/")) {
    const fileNameArr = filePath.split("/");
    fileName = fileNameArr[fileNameArr.length-1];
  }
  
  //选择要上传的文件的内容
  let fileContent: string = buf2String(readContentFromFile(filePath))

  let bodyStr = "";
  // 带参
  let verifyDic = new Map<string, string>();
  verifyDic.set("param1", "1111");
  verifyDic.set("param2", "222");
  
  verifyDic.forEach((value, key) => {
    console.info(key + " -- " + value);
    bodyStr += "--" + boundary + "\r\n";
    bodyStr += "Content-Disposition: form-data; name=\"" + key + "\"\r\n";
    bodyStr += "\r\n";
    bodyStr += value;
    bodyStr += "\r\n";
  });
  
  bodyStr += "--" + boundary + "\r\n";
  bodyStr += "Content-Disposition: form-data; name=\"file\";filename=\"" + fileName + "\"\r\n";
  bodyStr += "\r\n";
  bodyStr += fileContent;
  bodyStr += "\r\n";
  bodyStr += "--" + boundary + "--\r\n";

  return bodyStr;
}

⭐️如果对你有用的话,希望可以点点赞,感谢了⭐️

3.完整代码示例

import { common } from '@kit.AbilityKit';
import { fileIo } from '@kit.CoreFileKit';
import { systemDateTime } from '@kit.BasicServicesKit';

import fs from '@ohos.file.fs';
import { util } from "@kit.ArkTS";

import http from '@ohos.net.http';

let context = getContext(this) as common.UIAbilityContext
let filesDir = context.filesDir;

let filePath = filesDir + '/test.txt' //文件路径

// 上传文件使用的分隔符
let boundary: string = '----Boundary';

// 写入文件
function writeFile(filePath: string){
  // 用文件路径,创建一个文件流。w+: 覆盖式读写,无论该文件存在与否
  let fileStream = fileIo.createStreamSync(filePath, "w+");
  const content = "这是一段测试文件。‌ 写入时间: " + systemDateTime.getTime().toString();
  console.log("文件内容 : ", content);
  fileStream.writeSync(content);
  fileStream.close();
}

function getBodyData(): string{
  let fileName = "";
  if (filePath && filePath.includes("/")) {
    const fileNameArr = filePath.split("/");
    fileName = fileNameArr[fileNameArr.length-1];
  }
  //选择要上传的文件的内容
  let fileContent: string = buf2String(readContentFromFile(filePath))
  let bodyStr = "";

  // 带参
  let verifyDic = new Map<string, string>();
  verifyDic.set("param1", "1111");
  verifyDic.set("param2", "222");

  verifyDic.forEach((value, key) => {
    console.info(key + " -- " + value);
    bodyStr += "--" + boundary + "\r\n";
    bodyStr += "Content-Disposition: form-data; name=\"" + key + "\"\r\n";
    bodyStr += "\r\n";
    bodyStr += value;
    bodyStr += "\r\n";
  });

  bodyStr += "--" + boundary + "\r\n";
  bodyStr += "Content-Disposition: form-data; name=\"file\";filename=\"" + fileName + "\"\r\n";
  bodyStr += "\r\n";
  bodyStr += fileContent;
  bodyStr += "\r\n";
  bodyStr += "--" + boundary + "--\r\n";

  return bodyStr;
}

//从文件读取内容
function readContentFromFile(fileUri: string): ArrayBuffer {
  let buf = new ArrayBuffer(1024 * 4);
  let file = fs.openSync(fileUri, fs.OpenMode.READ_ONLY);
  let readLen = fs.readSync(file.fd, buf, { offset: 0 });
  let result = buf.slice(0, readLen)
  fs.closeSync(file);
  return result
}

//ArrayBuffer转utf8字符串
function buf2String(buf: ArrayBuffer) {
  let msgArray = new Uint8Array(buf);
  let textDecoder = util.TextDecoder.create("utf-8");
  return textDecoder.decodeWithStream(msgArray)
}

type RequestListener = (isRequestSuc: boolean, requestData: string) => void;

async function requestPost(url: string, bodyData: string | undefined, httpResponseBlock: RequestListener){
  let contentTypeStr = "multipart/form-data; boundary=" + boundary;
  let postOptions: http.HttpRequestOptions = {
    method: http.RequestMethod.POST,
    extraData: bodyData,
    header: {
      'Content-Type': contentTypeStr
    },
    connectTimeout: 60000,
  };
  console.info("requestUrl --- " + url);
  const httpRequest = http.createHttp();
  httpRequest.request(url, postOptions, (err: Error, data: http.HttpResponse) => {
    if (!err) {
      console.info('Result:' + data.result);
      // console.info('code:' + data.responseCode);
      // console.info('type:' + JSON.stringify(data.resultType));
      // console.info('header:' + JSON.stringify(data.header));
      if (httpResponseBlock) httpResponseBlock(true, data.result as string);
    } else {
      console.error('error:' + JSON.stringify(err));
      if (httpResponseBlock) httpResponseBlock(false, JSON.stringify(err));
    }
  });
}

writeFile(filePath);
// 代码调用
let url = "xxx";
let bodyData = getBodyData();
requestPost(url, bodyData, (isRequestSuc: boolean, requestData: string) => {
  if (isRequestSuc) {
    console.info('上传成功 --- ' + requestData);
  }else {
    console.error("请求失败 err --- " + requestData.toString());
  }
});

⭐️如果对你有用的话,希望可以点点赞,感谢了⭐️

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐