当前位置:   article > 正文

HarmonyOS开发实战系列:使用HttpRequest上传任意文件到服务端规范示例_harmonyos 文件上传

harmonyos 文件上传

 1. 前述文件上传功能简介

在前述文章鸿蒙开发实战系列-使用HttpRequest上传文件到服务端示例中,为简化起见,只描述了如何上传文本类型的文件到服务端,对文件的大小也有一定的限制,只能作为鸿蒙API演示使用,在实际开发中上传的文件类型多样,大小不一,本文将介绍一种适应性更广的方法,可以上传任何类型的文件到服务端,并且不限制文件的大小。

2. 上传任意文件类型示例

本示例运行后的界面如下所示:

cke_17594.jpeg

可以从图库选择文件或者选择任意文件,并且可以设置上传后的文件名,最后单击“上传”按钮即可上传到服务端

下面详细介绍创建该应用的步骤。

步骤1:创建Empty Ability项目。

步骤2:在module.json5配置文件加上对权限的声明:

  1. "requestPermissions": [
  2. {
  3. "name": "ohos.permission.INTERNET"
  4. }
  5. ]

这里添加了访问互联网的权限。

步骤3:在Index.ets文件里添加如下的代码:

  1. import http from '@ohos.net.http';
  2. import util from '@ohos.util';
  3. import fs from '@ohos.file.fs';
  4. import picker from '@ohos.file.picker';
  5. import systemDateTime from '@ohos.systemDateTime';
  6. import buffer from '@ohos.buffer';
  7. @Entry
  8. @Component
  9. struct Index {
  10. //连接、通讯历史记录
  11. @State msgHistory: string = ''
  12. //上传地址
  13. @State uploadUrl: string = "http://192.168.3.8:8081/upload"
  14. //上传后的文件名
  15. @State uploadFileName: string = ""
  16. //要上传的文件
  17. @State uploadFilePath: string = ""
  18. //是否允许上传
  19. @State canUpload: boolean = false
  20. scroller: Scroller = new Scroller()
  21. build() {
  22. Row() {
  23. Column() {
  24. Text("模拟上传示例")
  25. .fontSize(14)
  26. .fontWeight(FontWeight.Bold)
  27. .width('100%')
  28. .textAlign(TextAlign.Center)
  29. .padding(10)
  30. Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
  31. Text("上传的文件:")
  32. .fontSize(14)
  33. .width(100)
  34. .flexGrow(0)
  35. TextInput({ text: this.uploadFilePath })
  36. .enabled(false)
  37. .width(100)
  38. .fontSize(11)
  39. .flexGrow(1)
  40. }
  41. Flex({ justifyContent: FlexAlign.End, alignItems: ItemAlign.Center }) {
  42. Button("图库选择")
  43. .onClick(() => {
  44. this.selectImgFile()
  45. })
  46. .width(100)
  47. .fontSize(14)
  48. Button("其他文件")
  49. .onClick(() => {
  50. this.selectDocFile()
  51. })
  52. .width(100)
  53. .fontSize(14)
  54. }
  55. .width('100%')
  56. .padding(10)
  57. Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
  58. Text("上传地址:")
  59. .fontSize(14)
  60. .width(80)
  61. .flexGrow(0)
  62. TextInput({ text: this.uploadUrl })
  63. .onChange((value) => {
  64. this.uploadUrl = value
  65. })
  66. .width(110)
  67. .fontSize(11)
  68. .flexGrow(1)
  69. }
  70. .width('100%')
  71. .padding(10)
  72. Flex({ justifyContent: FlexAlign.Start, alignItems: ItemAlign.Center }) {
  73. Text("上传后文件名:")
  74. .fontSize(14)
  75. .width(100)
  76. .flexGrow(0)
  77. TextInput({ text: this.uploadFileName })
  78. .onChange((value) => {
  79. this.uploadFileName = value
  80. })
  81. .width(110)
  82. .fontSize(11)
  83. .flexGrow(1)
  84. Button("上传")
  85. .onClick(() => {
  86. this.uploadFile()
  87. })
  88. .enabled(this.canUpload)
  89. .width(70)
  90. .fontSize(14)
  91. .flexGrow(0)
  92. }
  93. .width('100%')
  94. .padding(10)
  95. Scroll(this.scroller) {
  96. Text(this.msgHistory)
  97. .textAlign(TextAlign.Start)
  98. .padding(10)
  99. .width('100%')
  100. .backgroundColor(0xeeeeee)
  101. }
  102. .align(Alignment.Top)
  103. .backgroundColor(0xeeeeee)
  104. .height(300)
  105. .flexGrow(1)
  106. .scrollable(ScrollDirection.Vertical)
  107. .scrollBar(BarState.On)
  108. .scrollBarWidth(20)
  109. }
  110. .width('100%')
  111. .justifyContent(FlexAlign.Start)
  112. .height('100%')
  113. }
  114. .height('100%')
  115. }
  116. //构造上传文件的body内容
  117. buildBodyContent(boundary: string, fileName: string, content: Uint8Array, contentType: string = "application/octet-stream") {
  118. let textEncoder = new util.TextEncoder();
  119. //构造文件内容前的部分
  120. let preFileContent = `--${boundary}\r\n`
  121. preFileContent = preFileContent + `Content-Disposition: form-data; name="file"; filename="${fileName}"\r\n`
  122. preFileContent = preFileContent + `Content-Type: ${contentType}\r\n`
  123. preFileContent = preFileContent + '\r\n'
  124. let preArray = textEncoder.encodeInto(preFileContent)
  125. //构造文件内容后的部分
  126. let aftFileContent = '\r\n'
  127. aftFileContent = aftFileContent + `--${boundary}`
  128. aftFileContent = aftFileContent + '--\r\n'
  129. let aftArray = textEncoder.encodeInto(aftFileContent)
  130. //文件前后内容和文件内容组合
  131. let bodyBuf = buffer.concat([preArray, content, aftArray])
  132. return bodyBuf.buffer
  133. }
  134. async copy2Sandbox(srcUri: string, fileName: string): Promise<string> {
  135. let context = getContext(this)
  136. //计划复制到的目标路径
  137. let realUri = context.cacheDir + "/" + fileName
  138. //复制选择的文件到沙箱cache文件夹
  139. try {
  140. let file = await fs.open(srcUri);
  141. fs.copyFileSync(file.fd, realUri)
  142. fs.close(file)
  143. } catch (err) {
  144. this.msgHistory += 'err.code : ' + err.code + ', err.message : ' + err.message;
  145. }
  146. return realUri
  147. }
  148. //上传文件
  149. async uploadFile() {
  150. //上传文件使用的分隔符
  151. let boundary: string = '----ShandongCaoxianNB666MyBabyBoundary' + (await systemDateTime.getCurrentTime(true)).toString()
  152. let sandFile = await this.copy2Sandbox(this.uploadFilePath, this.uploadFileName)
  153. //选择要上传的文件的内容
  154. let fileContent: Uint8Array = new Uint8Array(this.readContentFromFile(sandFile))
  155. //上传请求的body内容
  156. let bodyContent = this.buildBodyContent(boundary, this.uploadFileName, fileContent)
  157. //http请求对象
  158. let httpRequest = http.createHttp();
  159. let opt: http.HttpRequestOptions = {
  160. method: http.RequestMethod.POST,
  161. header: { 'Content-Type': `multipart/form-data; boundary=${boundary}`,
  162. 'Content-Length': bodyContent.byteLength.toString()
  163. },
  164. extraData: bodyContent
  165. }
  166. //发送上传请求
  167. httpRequest.request(this.uploadUrl, opt)
  168. .then((resp) => {
  169. this.msgHistory += "响应码:" + resp.responseCode + "\r\n"
  170. this.msgHistory += "上传成功\r\n"
  171. })
  172. .catch((e) => {
  173. this.msgHistory += "请求失败:" + e.message + "\r\n"
  174. })
  175. }
  176. //选择图库文件
  177. selectImgFile() {
  178. let imgPicker = new picker.PhotoViewPicker();
  179. imgPicker.select().then((result) => {
  180. if (result.photoUris.length > 0) {
  181. this.uploadFilePath = result.photoUris[0]
  182. this.msgHistory += "select file: " + this.uploadFilePath + "\r\n";
  183. this.canUpload = true
  184. let segments = this.uploadFilePath.split('/')
  185. //文件名称
  186. this.uploadFileName = segments[segments.length-1]
  187. }
  188. }).catch((e) => {
  189. this.msgHistory += 'PhotoViewPicker.select failed ' + e.message + "\r\n";
  190. });
  191. }
  192. //选择文件
  193. selectDocFile() {
  194. let documentPicker = new picker.DocumentViewPicker();
  195. documentPicker.select().then((result) => {
  196. if (result.length > 0) {
  197. this.uploadFilePath = result[0]
  198. this.msgHistory += "select file: " + this.uploadFilePath + "\r\n";
  199. this.canUpload = true
  200. let segments = this.uploadFilePath.split('/')
  201. //文件名称
  202. this.uploadFileName = segments[segments.length-1]
  203. }
  204. }).catch((e) => {
  205. this.msgHistory += 'DocumentViewPicker.select failed ' + e.message + "\r\n";
  206. });
  207. }
  208. //从文件读取内容
  209. readContentFromFile(fileUri: string): ArrayBuffer {
  210. let file = fs.openSync(fileUri, fs.OpenMode.READ_ONLY);
  211. let fsStat = fs.lstatSync(fileUri);
  212. let buf = new ArrayBuffer(fsStat.size);
  213. fs.readSync(file.fd, buf);
  214. fs.fsyncSync(file.fd)
  215. fs.closeSync(file);
  216. return buf
  217. }
  218. }

步骤4:编译运行,可以使用模拟器或者真机。

步骤5:选择文件,假设单击“图库选择”按钮,弹出图片选择窗口,选择一张图片,如图所示:

cke_136630.jpeg

步骤6:单击“完成”按钮,返回APP,然后修改上传后文件名,最后单击“上传”按钮上传,如图所示:

cke_231226.jpeg

步骤7:这样就完成了图片上传,在服务端可以看到上传后的图片:

cke_489937.jpg

这样就完成了任意文件的上传。

3. 上传功能分析

要实现上传功能,关键点在构造上传文件body内容,代码如下:

  1. //构造上传文件的body内容
  2. buildBodyContent(boundary: string, fileName: string, content: Uint8Array, contentType: string = "application/octet-stream") {
  3. let textEncoder = new util.TextEncoder();
  4. //构造文件内容前的部分
  5. let preFileContent = `--${boundary}\r\n`
  6. preFileContent = preFileContent + `Content-Disposition: form-data; name="file"; filename="${fileName}"\r\n`
  7. preFileContent = preFileContent + `Content-Type: ${contentType}\r\n`
  8. preFileContent = preFileContent + '\r\n'
  9. let preArray = textEncoder.encodeInto(preFileContent)
  10. //构造文件内容后的部分
  11. let aftFileContent = '\r\n'
  12. aftFileContent = aftFileContent + `--${boundary}`
  13. aftFileContent = aftFileContent + '--\r\n'
  14. let aftArray = textEncoder.encodeInto(aftFileContent)
  15. //文件前后内容和文件内容组合
  16. let bodyBuf = buffer.concat([preArray, content, aftArray])
  17. return bodyBuf.buffer
  18. }

这里把body分为三个部分,分别是上传文件内容前的部分、上传文件内容部分以及上传文件内容后的部分,最后把它们组合到一块,作为request方法options参数的extraData属性,如下所示:

  1. //http请求对象
  2. let httpRequest = http.createHttp();
  3. let opt: http.HttpRequestOptions = {
  4. method: http.RequestMethod.POST,
  5. header: { 'Content-Type': `multipart/form-data; boundary=${boundary}`,
  6. 'Content-Length': bodyContent.byteLength.toString()
  7. },
  8. extraData: bodyContent
  9. }


最后

有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?但是又不知道从哪里下手,而且学习时频繁踩坑,最终浪费大量时间。所以本人整理了一些比较合适的鸿蒙(HarmonyOS NEXT)学习路径和一些资料的整理供小伙伴学习

点击领取→纯血鸿蒙Next全套最新学习资料

希望这一份鸿蒙学习资料能够给大家带来帮助,有需要的小伙伴自行领取~~

一、鸿蒙(HarmonyOS NEXT)最新学习路线

有了路线图,怎么能没有学习资料呢,小编也准备了一份联合鸿蒙官方发布笔记整理收纳的一套系统性的鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,内容包含:(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(HarmonyOS NEXT)…等技术知识点。

获取以上完整版高清学习路线,请点击→纯血版全套鸿蒙HarmonyOS学习资料

二、HarmonyOS Next 最新全套视频教程

三、《鸿蒙 (OpenHarmony)开发基础到实战手册》

OpenHarmony北向、南向开发环境搭建

四、大厂面试必问面试题

五、鸿蒙南向开发技术

六、鸿蒙APP开发必备


完整鸿蒙HarmonyOS学习资料,请点击→纯血版全套鸿蒙HarmonyOS学习资料

总结
总的来说,华为鸿蒙不再兼容安卓,对中年程序员来说是一个挑战,也是一个机会。只有积极应对变化,不断学习和提升自己,他们才能在这个变革的时代中立于不败之地。 

                        

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/寸_铁/article/detail/976786
推荐阅读
相关标签
  

闽ICP备14008679号