大致思路
利用图片选择器打开相册将选中的图片存(复制)到沙箱中,将这个图片(以类型,文件名,uri定义图片的唯一性)上传(request/axios)
一,选择图片
步骤
- 用photoAccessHelper创建一个图片选择器的行为PhotoSelectOptions
- 约束选择图片的条件,选择图片的最大数量,及图片类型
- 在用photoAccessHelper创建一个图片选择器PhotoViewPicker
- 用异步方法photoPicker.select()获取用户的选择结果
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { BusinessError } from '@kit.BasicServicesKit';
@Entry
@Component
struct Index {
// 图片的uri
uri: string = ""
// 自定义生成的文件名
fileName: string = ""
// 图片类型
fileType: string = "jpg"
build() {
Column() {
Button("选择图片")
.onClick(async () => {
try {
// 创建一个新的 PhotoSelectOptions 实例来配置图片选择器的行为
let PhotoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
// 设置 MIME 类型为图像类型,这样用户只能选择图像文件
PhotoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
// 设置用户可以选择的最大图片数量为 1 张
PhotoSelectOptions.maxSelectNumber = 1;
// PhotoSelectOptions.isSearchSupported = false
// 创建一个新的 PhotoViewPicker 实例,用于打开图片选择器
let photoPicker = new photoAccessHelper.PhotoViewPicker();
// 使用前面配置好的选项打开图片选择器,并等待用户完成选择
// 注意这里的 select 方法是一个异步方法,所以需要使用 await 关键字等待其结果
const PhotoSelectResult = await photoPicker.select(PhotoSelectOptions);
// 获取用户选择的第一张图片的 URI(统一资源标识符)
// 假设这里只关心用户选择的第一张图片
this.uri = PhotoSelectResult.photoUris[0];
} catch (error) {
// 如果在 try 块中发生了错误,则会在这里捕获到这个错误
// 将捕获到的错误转换成 BusinessError 类型(假设 error 是一个继承自 Error 的自定义错误类型)
// 在控制台中打印错误信息,包括错误码和错误消息
console.error(`PhotoViewPicker failed with err: ${error.code}, ${error.message}`);
}
})
}
.width("100%")
.height("100%")
.justifyContent(FlexAlign.Center)
}
}
拷贝图片到缓存目录
为了只能上传存在缓存目录的文件
预览模拟器中的文件
/data/app/el2/100/base/你的项目的boundle Name 即可 /haps/entry/cache
- 获取上下文对象
- 拼接生成唯一的图片名称 例如:时间戳.jpg
- 用filo.openSync()来获取指定图片的读取权限
- 生成图片储存的路径
- 用fileIo.copyFileSync()将待复制的文件复制到指定路径
{
try {
// 三.将文件保存到缓存目录(只能上传在缓存目录中的文件)
const context = getContext(this) // 上下文对象 程序运行过程中
// 生成一个新的文件名 234342343243.jpg
this.fileName = Date.now() + '.' + this.fileType
// 获取这个相册图片文件的权限 读取权限
const file = fileIo.openSync(this.uri, fileIo.OpenMode.READ_ONLY)
// 将文件 拷贝到 临时目录
// 通过缓存路径+文件名 拼接出完整的路径
// /data/storage/el2/base/haps/entry/cache/ 1726038971043.jpg
const copyFilePath = context.cacheDir + '/' + this.fileName
// 拷贝文件
// file.fd 待复制的文件 文件描述符 数字
// copyFilePath 文件复制到哪去
fileIo.copyFileSync(file.fd, copyFilePath)
} catch (e) {
console.log("e", JSON.stringify(e))
}
}
三,文件上传
- 用数组储存设置文件上传的信息(文件名,类型,路径......等等等)
- 用请求接口的方式发送文件信息
- 上传成功会返回图片上传的地址
// 获取上下文,通常用于获取应用运行时环境或上下文信息
const context = getContext(this);
// 定义一个文件数组,其中包含要上传的文件信息
let files: Array<request.File> = [
{
// 文件名 234342343243.jpg
filename: this.fileName,
// 文件类型
type: this.fileType,
// 看接口文档来确定
name: 'img',
// 文件的 URI,内部缓存路径加上文件名
uri: `internal://cache/${this.fileName}`
}
];
// 发起文件上传请求
request.uploadFile(context, {
// 上传接口的 URL 地址
url: 'https://hmajax.itheima.net/api/uploadimg',
// HTTP 请求方法
method: http.RequestMethod.POST,
// 请求头,设置内容类型为 multipart/form-data,适用于文件上传
header: {
// multipart/form-data 当前的参数 是一个文件!!!
contentType: 'multipart/form-data',
},
// 文件信息数组
files,
// 额外提交的数据,虽然这里为空数组,但根据接口要求不能省略
data: []
})
.then(res => {
// 监听 headerReceive 事件,当接收到响应头部时触发
res.on('headerReceive', (value) => {
// 解析并处理响应数据
// 定义一个接口描述返回的数据结构
interface I {
url: string;
}
interface II2 {
// code: number
data: I;
message: string;
}
interface II3 {
body: string;
}
// 解析响应体中的数据
const parsedBody = JSON.parse((value as II3).body) as II2;
// 输出上传成功的文件 URL
console.log("上传的地址为:", parsedBody.data.url);
// 可以在此处添加其他逻辑,如显示对话框等
// AlertDialog.show({
// message: parsedBody.data.url
// });
});
})
.catch((err: Error) => {
console.log("err", JSON.stringify(err))
})
四,归总流程
详细
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { BusinessError, request } from '@kit.BasicServicesKit';
import { fileIo } from '@kit.CoreFileKit';
import { http } from '@kit.NetworkKit';
// fs.copyFileSync
@Entry
@Component
struct Index {
// 图片的uri
uri: string = ""
// 自定义生成的文件名
fileName: string = ""
// 图片类型
fileType: string = "jpg"
build() {
Column() {
Button("1 选择图片")
.onClick(async () => {
try {
// 创建一个新的 PhotoSelectOptions 实例来配置图片选择器的行为
let PhotoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
// 设置 MIME 类型为图像类型,这样用户只能选择图像文件
PhotoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
// 设置用户可以选择的最大图片数量为 1 张
PhotoSelectOptions.maxSelectNumber = 1;
// PhotoSelectOptions.isSearchSupported = false
// 创建一个新的 PhotoViewPicker 实例,用于打开图片选择器
let photoPicker = new photoAccessHelper.PhotoViewPicker();
// 使用前面配置好的选项打开图片选择器,并等待用户完成选择
// 注意这里的 select 方法是一个异步方法,所以需要使用 await 关键字等待其结果
const PhotoSelectResult = await photoPicker.select(PhotoSelectOptions);
// `file://media/Photo/1/IMG_1725939294_000/screenshot_20240910_113314.jpg`
// 假设这里只关心用户选择的第一张图片
this.uri = PhotoSelectResult.photoUris[0];
} catch (error) {
// 如果在 try 块中发生了错误,则会在这里捕获到这个错误
// 将捕获到的错误转换成 BusinessError 类型(假设 error 是一个继承自 Error 的自定义错误类型)
// 在控制台中打印错误信息,包括错误码和错误消息
console.error(`PhotoViewPicker failed with err: ${error.code}, ${error.message}`);
}
})
Button(`2 拷贝图片到缓存目录中`)
.onClick(() => {
try {
// 三.将文件保存到缓存目录(只能上传在缓存目录中的文件)
const context = getContext(this) // 上下文对象 程序运行过程中
// 生成一个新的文件名 234342343243.jpg
this.fileName = Date.now() + '.' + this.fileType
// 获取这个相册图片文件的权限 读取权限
const file = fileIo.openSync(this.uri, fileIo.OpenMode.READ_ONLY)
// 将文件 拷贝到 临时目录
// 通过缓存路径+文件名 拼接出完整的路径
// /data/storage/el2/base/haps/entry/cache/ 1726038971043.jpg
const copyFilePath = context.cacheDir + '/' + this.fileName
// 拷贝文件
// file.fd 待复制的文件 文件描述符 数字
// copyFilePath 文件复制到哪去
fileIo.copyFileSync(file.fd, copyFilePath)
} catch (e) {
console.log("e", JSON.stringify(e))
}
})
Button(`3 上传图片`)
.onClick(() => {
// 获取上下文,通常用于获取应用运行时环境或上下文信息
const context = getContext(this);
// 定义一个文件数组,其中包含要上传的文件信息
let files: Array<request.File> = [
{
// 文件名 234342343243.jpg
filename: this.fileName,
// 文件类型
type: this.fileType,
// 看接口文档来确定
name: 'img',
// 文件的 URI,内部缓存路径加上文件名
uri: `internal://cache/${this.fileName}`
}
];
// 发起文件上传请求
request.uploadFile(context, {
// 上传接口的 URL 地址
url: 'https://hmajax.itheima.net/api/uploadimg',
// HTTP 请求方法
method: http.RequestMethod.POST,
// 请求头,设置内容类型为 multipart/form-data,适用于文件上传
header: {
// multipart/form-data 当前的参数 是一个文件!!!
contentType: 'multipart/form-data',
},
// 文件信息数组
files,
// 额外提交的数据,虽然这里为空数组,但根据接口要求不能省略
data: []
})
.then(res => {
// 监听 headerReceive 事件,当接收到响应头部时触发
res.on('headerReceive', (value) => {
// 解析并处理响应数据
// 定义一个接口描述返回的数据结构
interface I {
url: string;
}
interface II2 {
// code: number
data: I;
message: string;
}
interface II3 {
body: string;
}
// 解析响应体中的数据
const parsedBody = JSON.parse((value as II3).body) as II2;
// 输出上传成功的文件 URL
console.log("上传的地址为:", parsedBody.data.url);
// 可以在此处添加其他逻辑,如显示对话框等
// AlertDialog.show({
// message: parsedBody.data.url
// });
});
})
.catch((err: Error) => {
console.log("err", JSON.stringify(err))
})
})
}
.width("100%")
.height("100%")
.justifyContent(FlexAlign.Center)
}
}
// let str = `file://media/Photo/1/IMG_1725939294_000/screenshot_20240910_113314.jpg`
// // 获取 . 所在字符串内的下标 位置
//
// // let index=str.indexOf('.') // 左->右
// let index = str.lastIndexOf('.') // 右 -> 左
//
// // 从index位置开始裁剪,一直到结束 裁剪后的字符串给你返回
// const newStr = str.slice(index + 1)
// console.log("newStr", newStr)
// // console.log('index', index)
封装后的
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { fileIo } from '@kit.CoreFileKit';
import { request } from '@kit.BasicServicesKit';
import { http } from '@kit.NetworkKit';
@Entry
@Component
struct Index {
async aaa() {
const promise = new Promise<string>(async (resolve) => {
// 创建一个新的 PhotoSelectOptions 实例来配置图片选择器的行为
let PhotoSelectOptions = new photoAccessHelper.PhotoSelectOptions();
// 设置 MIME 类型为图像类型,这样用户只能选择图像文件
PhotoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE;
// 设置用户可以选择的最大图片数量为 1 张
PhotoSelectOptions.maxSelectNumber = 1;
// PhotoSelectOptions.isSearchSupported = false
// 创建一个新的 PhotoViewPicker 实例,用于打开图片选择器
let photoPicker = new photoAccessHelper.PhotoViewPicker();
// 使用前面配置好的选项打开图片选择器,并等待用户完成选择
// 注意这里的 select 方法是一个异步方法,所以需要使用 await 关键字等待其结果
const PhotoSelectResult = await photoPicker.select(PhotoSelectOptions);
// `file://media/Photo/1/IMG_1725939294_000/screenshot_20240910_113314.jpg`
// 假设这里只关心用户选择的第一张图片
let uri = PhotoSelectResult.photoUris[0];
// ======================================
// 三.将文件保存到缓存目录(只能上传在缓存目录中的文件)
let fileType = "jpg"
const context = getContext(this) // 上下文对象 程序运行过程中
// 生成一个新的文件名 234342343243.jpg
let fileName = Date.now() + '.' + fileType
// 获取这个相册图片文件的权限 读取权限
const file = fileIo.openSync(uri, fileIo.OpenMode.READ_ONLY)
// 将文件 拷贝到 临时目录
// 通过缓存路径+文件名 拼接出完整的路径
// /data/storage/el2/base/haps/entry/cache/ 1726038971043.jpg
const copyFilePath = context.cacheDir + '/' + fileName
// 拷贝文件
// file.fd 待复制的文件 文件描述符 数字
// copyFilePath 文件复制到哪去
fileIo.copyFileSync(file.fd, copyFilePath)
// =====================================
// 获取上下文,通常用于获取应用运行时环境或上下文信息
// 定义一个文件数组,其中包含要上传的文件信息
let files: Array<request.File> = [
{
// 文件名 234342343243.jpg
filename: fileName,
// 文件类型
type: fileType,
// 看接口文档来确定
name: 'img',
// 文件的 URI,内部缓存路径加上文件名
uri: `internal://cache/${fileName}`
}
];
// 发起文件上传请求
const res = await request.uploadFile(context, {
// 上传接口的 URL 地址
url: 'https://hmajax.itheima.net/api/uploadimg',
// HTTP 请求方法
method: http.RequestMethod.POST,
// 请求头,设置内容类型为 multipart/form-data,适用于文件上传
header: {
// multipart/form-data 当前的参数 是一个文件!!!
contentType: 'multipart/form-data',
},
// 文件信息数组
files,
// 额外提交的数据,虽然这里为空数组,但根据接口要求不能省略
data: []
})
// 监听 headerReceive 事件,当接收到响应头部时触发
res.on('headerReceive', (value) => {
// 解析并处理响应数据
// 定义一个接口描述返回的数据结构
interface I {
url: string;
}
interface II2 {
// code: number
data: I;
message: string;
}
interface II3 {
body: string;
}
// 解析响应体中的数据
const parsedBody = JSON.parse((value as II3).body) as II2;
// 输出上传成功的文件 URL
// console.log("上传的地址为:", parsedBody.data.url);
resolve(parsedBody.data.url)
});
// resolve("xxx")
})
return promise
}
@State
imgSrc: string = ''
build() {
Column() {
Image(this.imgSrc)
.width(100)
Button("直接图片上传")
.onClick(async () => {
// 返回 上传后的url给我即可
const result = await this.aaa()
// result 包含上传成功的url
console.log("result", result)
this.imgSrc = result
})
}
.width("100%")
.height("100%")
.justifyContent(FlexAlign.Center)
}
}
暂时无法在飞书文档外展示此内容