野外泛在线考核系统(十三)
三、电脑端+平板
接着(十二)这段时间主要是解决两个问题:
1.平板端图片上传至电脑服务端的问题,同时将图片名称,地址,关联ID等写入数据库
2.平板端设计登录验证环节,通过登录确定使用者身份,而后从服务端下载考核任务,进行评分,评分完毕再次将评分成绩,拍摄的照片等上传服务器
(一)平板端上传图片至电脑服务端
- 平板端主要参考了新闻发布的例子:
- 上传文件用到了FileUtil.ets这个文件
主要包含fileSelect()和fileUpload()两个函数
/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import fs from '@ohos.file.fs';
import request from '@ohos.request';
import picker from '@ohos.file.picker';
import {CommonConstants as Const, ContentType, RequestMethod, UploadingState } from '../constants/CommonConstants';
import ResponseResult from '../bean/ResponseResult';
import Logger from './Logger';
import { showToast } from './ToastUtil';
/**
* PhotoViewPicker
* 在原来基础上改进,可以一次选择多个图片
* @returns uri The uri for the selected file.
*/
export async function fileSelect(): Promise<string[]> {
let photoSelectOptions = new picker.PhotoSelectOptions();
photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE;
photoSelectOptions.maxSelectNumber = 2; //一次选择图片的上限
let photoPicker = new picker.PhotoViewPicker();
try {
let photoSelectResult = await photoPicker.select(photoSelectOptions);
if (photoSelectResult && photoSelectResult.photoUris && photoSelectResult.photoUris.length > 0) {
let imgUri = photoSelectResult.photoUris;
if (imgUri[0].indexOf('media/image') < 0) { //为什么是这个路径我没有搞懂,可能选择结果都是存在平板的这个目录中
showToast($r('app.string.prompt_select_img'));
return [''];
}
return imgUri;
} else {
return [''];
}
} catch (err) {
Logger.error('SelectedImage failed', JSON.stringify(err));
return [''];
}
}
/**
* Upload file.
*
* @param context Indicates the application BaseContext.
* @param fileUri The local storage path of the file.
* @returns the promise returned by the function.
*/
export function fileUpload(context: Context, fileUri: string[],content_id:string): Promise<ResponseResult> {
// Obtaining the Application File Path.
let imgName:string[]=[]; //数组申明必须初始化,不然引用会报错
let dstPath:string[]=[];
let fileUpload:Array<request.File>=[];
let cacheDir = context.cacheDir;
let index:number = 0;
for (let fileU of fileUri) {
imgName[index] = fileU.split('/').pop() + '.jpg';
dstPath[index] = cacheDir + '/' + imgName[index];
fileUpload[index] = {
filename: imgName[index],
name: 'contentImages',
uri: 'internal://cache/' + imgName[index],
type: 'jpg'
}
index += 1;
}
let url: string = Const.SERVER + Const.UPLOAD_FILE;
let uploadRequestOptions: request.UploadConfig = {
url: url,
header: {
'Content-Type': ContentType.FORM
},
method: RequestMethod.POST,
files: fileUpload,
data: [{ name: "content", value: content_id }]
};
let serverData = new ResponseResult();
return new Promise((resolve: Function, reject: Function) => {
try {
// Copy the URI to the cacheDir directory and upload the file.
// 将URI复制到cacheDir目录并上传文件
let i:number = 0;
for(let fileU of fileUri){
let srcFile = fs.openSync(fileU);
let dstFile = fs.openSync(dstPath[i], fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
fs.copyFileSync(srcFile.fd, dstFile.fd);
fs.closeSync(srcFile);
fs.closeSync(dstFile);
Logger.info('check-data1-i :',i.toString());
i +=1;
}
// Upload the file.
request.uploadFile(context, uploadRequestOptions).then((data: request.UploadTask) => {
data.on(UploadingState.COMPLETE, (result: Array<request.TaskState>) => {
Logger.info('uploadFile success', JSON.stringify(result));
if (result && result.length >= 1) {
serverData.code = Const.SERVER_CODE_SUCCESS;
serverData.msg = result[0].message;
serverData.data = Const.IMAGE_PREFIX + result[0].path;
resolve(serverData);
}
});
data.on(UploadingState.FAIL, (result: Array<request.TaskState>) => {
Logger.info('uploadFile failed', JSON.stringify(result));
if (result && result.length >= 1) {
serverData.msg = $r('app.string.upload_error_message');
reject(serverData);
}
})
}).catch((err: Error) => {
Logger.error('uploadFile failed', JSON.stringify(err));
reject(serverData);
});
} catch (err) {
Logger.error('uploadFile failed', JSON.stringify(err));
reject(serverData);
}
})
}
- 新闻发布的例子中是将图片和提交的信息分开提交的,图片使用request.uploadFile(context, uploadRequestOptions)方法,其他信息则是使用httpRequest(url, http.RequestMethod.POST, newsData)方法,其实在uploadFile中在上传图片的时候也可以额外传递数据,方法就是将数据通过request.UploadConfig中的data来传递,可以参考@ohos.request (上传下载)的示例,我这里通过data传递了一个名为content,值为content_id的量,注意,content_id必须为string类型的。因为 ‘Content-Type’: ContentType.FORM, method: RequestMethod.POST,Django服务端获取数据可通过request.POST.get(‘content’)得到content_id的值。
let uploadRequestOptions: request.UploadConfig = {
url: url,
header: {
'Content-Type': ContentType.FORM
},
method: RequestMethod.POST,
files: fileUpload,
data: [{ name: "content", value: content_id }]
};
export const enum RequestMethod {
POST = 'POST',
GET = 'GET'
}
export const enum ContentType {
JSON = 'application/json',
FORM = 'multipart/form-data'
}
- 特别注意的是,如果平板端提交数据时,‘Content-Type’: ContentType.JSON,method:RequestMethod.POST,那么Django服务端只能通过json.loads(request.body.decode('utf-8))的方式获取数据,即两边都采用json格式发送接收数据。
(二)平板端登录验证
- 为了达到登录验证效果,需要在平板创建一个数据表用来保存账户信息,于是参照之前野外泛在线考核系统(十)的方法创建TeacherTable表,结果死活写不进数据,也没有报错。
- 经过一天的排查,终于找到问题:就是SQL语句有错。下面两段唯一的差别就在registerDate前面的那个“,”一个是中文的,一个时英文的,结果执行是中文的也不报错,奔溃!但是后面就是插入不进数据,应该是表没有创建成功。
'teacherName TEXT,teacherAccount TEXT,teacherPassword TEXT,registerDate TEXT)',
'teacherName TEXT,teacherAccount TEXT,teacherPassword TEXT,registerDate TEXT)',
- 为了避免踩坑,可以将SQL语句提交给百度的文心一言检查下。切记切记!!