HarmonyOS鸿蒙Next中request.uploadFile获取不到返回值,上传没效果(可有偿)
HarmonyOS鸿蒙Next中request.uploadFile获取不到返回值,上传没效果(可有偿)
上传文件,不知道上次成功还是失败,没有接口返回值找不到问题所在。大佬指教下,真没招了。
可以加联系方式V,可有偿:Rik-Q1129
代码稍微改动了下。使用的是api13
现在“uploadTask参数”打印出来是空对象,走的catch。
import { common } from "@kit.AbilityKit";
import fs from '@ohos.file.fs';
import { BusinessError, request } from "@kit.BasicServicesKit";
import { photoAccessHelper } from "@kit.MediaLibraryKit";
// 获取应用文件路径
let context = getContext(this) as common.UIAbilityContext;
let cacheDir = context.cacheDir;
/**
* 从相册中获取文件
*/
export async function PickerPhoto(){
// const options = new picker.PhotoSelectOptions() //api-12已弃用
const options = new photoAccessHelper.PhotoSelectOptions
//最多选择一个
options.maxSelectNumber = 1;
//文件类型
options.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_TYPE
//获取选中的图片地址
const optionsView = new photoAccessHelper.PhotoViewPicker()
let urls = await optionsView.select(options)
// 没有选择的情况
if(urls.photoUris.length<=0)return;
let imgUrl = urls.photoUris[0]
return imgUrl
}
/**
* 将本地图片 文件拷贝到cache缓存目录
*/
export function copyImgToCache(photoImgPath:string){
const file = fs.openSync(photoImgPath,fs.OpenMode.READ_ONLY)
// 文件唯一标识
let fileFD = file.fd
let fileName = Date.now().toString() //避免文件重复,重新文件名
const ext = 'jpg'
let fullPath = cacheDir + "/" + fileName + "." + ext;
fs.copyFileSync(fileFD,fullPath) //拷贝文件与路径
return [`internal://cache/${fileName + '.' + ext}`,fileName + "." + ext]
}
/**
* 封装上传任务为Promise
*/
function uploadTaskToPromise(uploadTask: request.UploadTask): Promise<void> {
return new Promise((resolve, reject) => {
uploadTask.on('complete', (taskStates: Array<request.TaskState>) => {
console.log('completeTaskStates',JSON.stringify(taskStates))
resolve()
});
uploadTask.on('fail', (taskStates: Array<request.TaskState>) => {
console.log('failTaskStates',JSON.stringify(taskStates))
reject(new Error(`上传失败: ${JSON.stringify(taskStates)}`));
});
});
}
/**
* 上传文件
*/
interface OssData{
key:string
policy:string
uuid:string
credential:string
timeStamp:string
signature:string
region:string
}
export async function FileUpload(
bucketName: string,
imgSrc: string | undefined,
ossData: OssData
): Promise<void> {
if (!imgSrc) {
throw new Error('未选择图片');
}
const fileInfo = copyImgToCache(imgSrc);
const fileUri = fileInfo[0];
const fileName = fileInfo[1];
// 注意:文件字段必须是最后一个参数!
const formData: Array<request.RequestData> = [
{ name: 'key', value: ossData.key },
{ name: 'policy', value: ossData.policy },
{ name: 'x-amz-meta-uuid', value: ossData.uuid },
{ name: 'x-amz-server-side-encryption', value: 'AES256' },
{ name: 'X-Amz-Credential', value: ossData.credential },
{ name: 'X-Amz-Algorithm', value: 'AWS4-HMAC-SHA256' },
{ name: 'X-Amz-Date', value: ossData.timeStamp },
{ name: 'X-Amz-Signature', value: ossData.signature },
{ name: 'acl', value: 'public-read' },
// 不要包含 Content-Type,由系统自动生成
];
const uploadConfig: request.UploadConfig = {
url: `https://${bucketName}.s3.${ossData.region}.amazonaws.com/`,
header: {
'Authorization': AppStorage.get('Token') || '',
'lang': AppStorage.get('language') || 'en',
// 不要手动设置 Content-Type
},
method: 'POST',
files: [{
filename: fileName,
name: 'file', // 必须是'file'
uri: fileUri,
type: 'image/jpeg' // 根据实际类型调整
}],
data: formData
};
try {
console.log('上传前uploadConfig',JSON.stringify(uploadConfig))
const uploadTask = await request.uploadFile(context, uploadConfig);
console.log('context--------',JSON.stringify(context));
console.log('uploadTask参数',JSON.stringify(uploadTask));
return await uploadTaskToPromise(uploadTask);
} catch (err) {
console.error('上传任务创建失败', JSON.stringify(err));
throw new Error('文件上传失败');
}
}
更多关于HarmonyOS鸿蒙Next中request.uploadFile获取不到返回值,上传没效果(可有偿)的实战教程也可以访问 https://www.itying.com/category-93-b0.html
【背景知识】
- JSON.stringify该方法将一个ArkTS对象或数组转换为JSON字符串,支持线性容器的转换,不支持非线性容器。
- request.uploadFile创建并启动一个上传任务,使用Promise异步回调,支持HTTP协议。通过on(‘complete’|‘fail’)可获取任务上传时的成功信息或错误信息。
- ohos.net.http (数据请求)提供HTTP数据请求能力。应用可以通过HTTP发起一个数据请求,支持常见的GET、POST、OPTIONS、HEAD、PUT、DELETE、TRACE、CONNECT方法。
【问题分析】
request.uploadFile返回UploadTask类,只有成员函数,JSON.stringify会忽略函数的打印,因此打印JSON.stringify(uploadTask)会打印出来为空,经过线下沟通,楼主实际是通过请求特定的API接口上传文件。而不是通过通用的http协议去上传文件。
【解决方案】
经过上述分析,使用httpRequest.request的请求方法上传文件。其请求方法为POST方法,因为要上传文件请求体字段为文件和字符串的混合,因此请求体content-type类型为multipart/form-data,multiFormDataList中字符串contentType设置为空字符串,文件按照文件类型设置,关键代码如下:
async function FileUpload(
bucketName: string,
imgSrc: string | undefined,
cacheDir: string,
ossData: OssData
): Promise<void> {
if (!imgSrc) {
throw new Error('未选择图片');
}
const cacheInfo = copyImgToCache(imgSrc, cacheDir) as CacheBuffer;
const fileName = cacheInfo.fileName;
const imgArrayBuffer = cacheInfo.imgArrayBuffer;
let urlStr = `https://${bucketName}.s3.${ossData.region}.amazonaws.com/`
const httpRequest = http.createHttp();
let ContentType = 'multipart/form-data';
let HttpRequestOptions:http.HttpRequestOptions = {
method: http.RequestMethod.POST,
header: {
'Content-Type': ContentType,
},
connectTimeout: 60000, // 可选,默认为60000ms
readTimeout: 60000, // 可选,默认为60000ms
}
if (ContentType==='multipart/form-data'){
HttpRequestOptions.multiFormDataList = [
{
name: "acl",
contentType: '',
data: 'public-read',
},
{
name: "key",
contentType: '',
data: ossData.key,
},
{
name: "policy",
contentType: '',
data: ossData.policy,
},
{
name: "Content-Type",
contentType: '',
data: ContentType,
},
{
name: "x-amz-meta-uuid",
contentType: '',
data: ossData.uuid,
},
{
name: "x-amz-server-side-encryption",
contentType: '',
data: 'AES256',
},
{
name: "X-Amz-Credential",
contentType: '',
data: ossData.credential,
},
{
name: "X-Amz-Algorithm",
contentType: '',
data: 'AWS4-HMAC-SHA256',
},
{
name: "X-Amz-Date",
contentType: '',
data: ossData.timeStamp,
},
{
name: "X-Amz-Signature",
contentType: '',
data: ossData.signature,
},
{
name: "file",
contentType: 'image/jpeg',
data: imgArrayBuffer,
remoteFileName: fileName
}
]
}
console.log('HttpRequestOptions:',JSON.stringify(HttpRequestOptions))
httpRequest.request(urlStr, HttpRequestOptions, (error: Error, data: http.HttpResponse) => {
console.log('请求接口完成',JSON.stringify(data))
if (!error) { //请求成功
console.log('请求接口成功',JSON.stringify(data))
httpRequest.destroy(); //成功或失败都要销毁请求
} else {
console.log(`请求失败 ${JSON.stringify(error)}`)
httpRequest.destroy();
}
})
}
【总结】
如果在实际应用中服务端有指定的API请求接口,上传图片等文件应使用httpRequest.request方法使用POST请求资源,上传文件时其content-Type需要设置为multipart/form-data。
更多关于HarmonyOS鸿蒙Next中request.uploadFile获取不到返回值,上传没效果(可有偿)的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
问题处理了很久一直未得到解决,头皮都掉了一层。老哥加班加点,并且非常有耐心的帮忙一起寻找问题,一点点的解刨问题、分析报错等。期间加班加点的花了不少时间,一直不辞辛苦,直到问题解决。
非常感谢这位老哥,不仅是因为帮我解决了一个对我来讲比较难得难点,期间帮我分析问题,处理好后,那些地方有什么问题,为什么有这个问题都一一给我讲解,让我在技术层面也有进步。
上传文件的URI必须使用internal://cache/协议,但楼主代码中生成的路径未包含完整缓存目录路径。files配置中type字段应为完整的MIME类型,而非文件扩展名jpg,错误设置会导致服务器无法识别文件类型。
手动设置"Content-Type":"multipart/form-data;boundary=…"会覆盖系统自动生成的合法边界值,导致服务器报错not a multipart request,应移除手动设置。
// 修正后的files配置
let files: Array<request.File> = [
{
filename: array,
name: 'file',
uri: array,
type: 'image/jpeg' // 修正为合法MIME类型
}
]
// 修正后的header配置
header: {
'Authorization': AppStorage.get('Token'),
'lang': AppStorage.get('language')
// 删除手动设置的Content-Type参数
},
为啥不用成熟的网络框架:[https://ohpm.openharmony.cn/#/cn/detail/@ohos%2Faxios](https://ohpm.openharmony.cn/#/cn/detail/@ohos%2Faxios)
一般上传文件都需要先把本地文件解析成字节流,然后再发送到服务器,需要从这里入手,看你代码都是直接传的文件名
找HarmonyOS工作还需要会Flutter的哦,有需要Flutter教程的可以学学大地老师的教程,很不错,B站免费学的哦:https://www.bilibili.com/video/BV1S4411E7LY/?p=17
看你手动设置了 Content-Type: "multipart/form-data;boundary=xxx"
,
这个应该是没有必要的,你可以去掉试试,参考 API
uploadTask.on('complete', (taskStates: Array<request.TaskState>) => {
console.log('查询上传参数',JSON.stringify(taskStates))
for (let i = 0; i < taskStates.length; i++) {
console.info(`上传成功: ${JSON.stringify(taskStates[i])}`);
}
});
这个方法执行了
JSON.stringify(taskStates)
这个没有值。
console.info(
上传成功: ${JSON.stringify(taskStates[i])});
在这打印出来是undefind
uploadTask.on('progress',(progress) =>{
console.info("progress headers:" + JSON.stringify(progress));
})
这个进度能看到返回值。
uploadTask.on('fail', (taskStates: Array<request.TaskState>) => {
for (let i = 0; i < taskStates.length; i++) {
console.info("upOnFail taskState:" + JSON.stringify(taskStates[i]));
}
})
在HarmonyOS Next中,request.uploadFile获取不到返回值可能是由于以下原因:
- 未正确配置网络权限(ohos.permission.INTERNET)
- 上传地址或参数格式错误
- 回调函数未正确定义
- 文件路径无效或权限不足
检查代码示例:
request.uploadFile({
url: 'https://example.com/upload',
files: [{uri: 'file://...'}],
success: (res) => {console.log(res)},
fail: (err) => {console.error(err)}
});
从代码和描述来看,上传失败的主要问题可能是:
-
上下文(context)传递问题:确保传递给uploadFile的context是正确的UIAbilityContext实例。打印的context显示为空,这可能是根本原因。
-
文件URI格式问题:internal://cache/路径可能需要使用正确的URI格式,建议使用完整的file://路径。
-
权限检查:确保应用已申请ohos.permission.INTERNET和ohos.permission.READ_MEDIA权限。
-
服务器配置:检查上传URL和OSS参数是否正确,特别是region和bucketName的拼接。
建议先检查context是否正确获取,可以在调用uploadFile前打印context验证。同时检查网络请求是否被拦截,使用抓包工具确认请求是否发出。文件路径建议使用更明确的file://格式。