ArkTS(3.0與3.1)前端和SpringBoot后端文件上傳示例(Request.upload)
前言
此帖主要講解通過開發(fā)文檔示例代碼寫一個完整Demo,方便初學(xué)者理解開發(fā)文檔內(nèi)容,大家都知道3.0使用的是FA模式、3.1使用的是Stage模式,所以同樣是文件上傳,代碼寫法上有些不一樣,開發(fā)文檔也不一樣,比如在3.1下,可以在HarmonyOS Developer > 文檔 > 指南 > 開發(fā)下找到文件上傳下載示例代碼,而在3.0下,就找不到相應(yīng)指南開發(fā)了,只能在HarmonyOS Developer > 文檔 > API參考 > ArkTS API參考找到@ohos.request (上傳下載)文檔,為了實(shí)現(xiàn)一個完整文件上傳Demo,后端是少不了的,這里我使用了我平常工作中用到的SpringBoot開發(fā)后端,為了驗證文件上傳接口是否正常,使用Thymeleaf寫一個簡單的前端頁面來測試接口,先保證后端文件上傳接口是正常的,這樣其它前端調(diào)用就可以排除后端文件上傳接口問題,專心調(diào)試前端代碼,希望小伙伴通過此貼學(xué)習(xí)到文件上傳同時,參考此思路也可以自己完成其它示例代碼完成Demo。
效果
知識點(diǎn)
ArkTS(3.0)文件管理(前端)
- 此版本使用的是FA模式、配置文件名是config.json 由于文件上傳需要網(wǎng)絡(luò),需要添加權(quán)限:ohos.permission.INTERNET,默認(rèn)支持https,如果要支持http,需要在config.json里增加network標(biāo)簽,屬性標(biāo)識 “cleartextTraffic”: true。所以config.json要添加的內(nèi)容以下:
{
"app": {...},
"deviceConfig": {
"default": {
"network": {
"cleartextTraffic": true
}
}
},
"module": {
"reqPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
}
}
- 文件上傳頁面就一個index.ets文件,里面包含UI和調(diào)用后端接口,代碼如下:
import request from '@ohos.request';
import featureAbility from '@ohos.ability.featureAbility';
import fileio from '@ohos.fileio';
@Entry
@Component
struct Index {
@State btnLabel: string = '提交文件'
private uploadTask: request.UploadTask
aboutToAppear() {
// 獲取應(yīng)用文件路徑
var context = featureAbility.getContext();
context.getCacheDir().then((data) => {
console.info("xx ======================>getCacheDirPromsie====================>");
console.info("xx ====>data====>" + JSON.stringify(data));
// 新建一個本地應(yīng)用文件
let fd = fileio.openSync(data + '/test.txt', 0o102, 0o666);
fileio.writeSync(fd, 'upload file test by army');
fileio.closeSync(fd);
});
}
aboutToDisappear() {
this.uploadTask.off("progress")
}
uploadFile() {
// 上傳任務(wù)配置項
let uploadConfig = {
url: 'http://111.114.238.134:8740/file/upload',
header: { key1: 'Content-Type', key2: 'multipart/form-data' },
method: 'POST',
files: [
{ filename: 'test.txt', name: 'test', uri: 'internal://cache/test.txt', type: 'txt' }
],
data: [
{ name: 'fileId', value: 'FP000008' }
]
}
// 將本地應(yīng)用文件上傳至網(wǎng)絡(luò)服務(wù)器
try {
this.btnLabel = '文件上傳中...'
request.upload(uploadConfig)
.then((data) => {
this.btnLabel = '文件上傳成功'
this.uploadTask = data
console.info('xx Success to request the upload. Cause: ' + JSON.stringify(data));
// uploadTask = data;
this.uploadTask.on("progress", (uploadedSize, totalSize) => {
console.info('xx 上傳進(jìn)度值是:' + uploadedSize + ', 總大小:' + totalSize)
})
}).catch((err) => {
this.btnLabel = '文件上傳失敗'
console.error('xx Failed to request the upload. Cause: ' + JSON.stringify(err));
})
} catch (err) {
this.btnLabel = '文件上傳失敗'
console.error(`xx Invoke uploadFile failed, code is ${err.code}, message is ${err.message}`);
}
}
build() {
Column({space: 30}) {
Text('上傳文件實(shí)例:')
.width('100%')
.height(50)
.fontSize(24)
.textAlign(TextAlign.Center)
Button('提交文件')
.onClick(() => {
this.uploadFile()
})
.width('80%')
.height(50)
.fontSize(24)
}.width('100%').height('100%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
}
}
ArkTS(3.1)文件管理(前端)
- 此版本使用的是Stage模式、配置文件名是module.json5 由于文件上傳需要網(wǎng)絡(luò),需要添加權(quán)限:ohos.permission.INTERNET,在3.1不用配置,就支持http和https,當(dāng)前上傳應(yīng)用文件功能,僅支持上傳應(yīng)用緩存文件路徑(cacheDir)下的文件。所以module.json5要添加的內(nèi)容以下:
{
"module": {
"requestPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
}
}
- 文件上傳頁面就一個index.ets文件,里面包含UI和調(diào)用后端接口,代碼如下:
import common from '@ohos.app.ability.common';
import fs from '@ohos.file.fs';
import request from '@ohos.request';
import hash from '@ohos.file.hash';
// 獲取應(yīng)用文件路徑
let context = getContext(this) as common.UIAbilityContext;
@Entry
@Component
struct Index {
@State btnLabel: string = '提交文件'
private uploadTask: request.UploadTask
aboutToAppear() {
let cacheDir = context.cacheDir;
// 新建一個本地應(yīng)用文件
let file = fs.openSync(cacheDir + '/test.txt', fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
fs.writeSync(file.fd, 'upload file test by API9');
fs.closeSync(file);
}
aboutToDisappear() {
this.uploadTask.off("complete")
}
uploadFile() {
// 上傳任務(wù)配置項
let uploadConfig = {
url: 'http://111.114.238.134:8740/file/upload',
header: { key1: 'Content-Type', key2: 'multipart/form-data' },
method: 'POST',
files: [
{ filename: 'test.txt', name: 'test', uri: 'internal://cache/test.txt', type: 'txt' }
],
data: [
{ name: 'fileId', value: 'FP000008' }
]
}
// 將本地應(yīng)用文件上傳至網(wǎng)絡(luò)服務(wù)器
try {
this.btnLabel = '文件上傳中...'
request.uploadFile(context, uploadConfig)
.then((data) => {
this.btnLabel = '文件上傳成功'
this.uploadTask = data
this.uploadTask.on('complete', (taskStates) => {
for (let i = 0; i < taskStates.length; i++) {
console.info(`xx upload complete taskState: ${JSON.stringify(taskStates[i])}`);
}
});
})
.catch((err) => {
this.btnLabel = '文件上傳失敗'
console.error(`xx Invoke uploadFile failed, code is ${err.code}, message is ${err.message}`);
})
} catch (err) {
this.btnLabel = '文件上傳失敗'
console.error(`xx Invoke uploadFile failed, code is ${err.code}, message is ${err.message}`);
}
}
build() {
Column({space:30}) {
Text('上傳文件實(shí)例:')
.width('100%')
.height(50)
.fontSize(24)
.textAlign(TextAlign.Center)
Button(this.btnLabel)
.onClick(() => {
this.uploadFile()
})
.width('80%')
.height(50)
.fontSize(24)
}
.width('100%').height('100%')
.justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center)
}
}
SpringBoot和Thymeleaf(后端)
- 后端首先列出pom.xml文件,里面包含項目依賴jar配置,比如web、thymeleaf依賴,內(nèi)容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.army</groupId>
<artifactId>file-manage</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>file-manage</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
- 前端調(diào)用接口文件Controller代碼如下:
@RestController
@RequestMapping("/file")
@Slf4j
public class FileController {
@Autowired
FileService fileService;
@PostMapping("/upload")
public StandardResponse upload(String fileId, MultipartHttpServletRequest multiPartRequest) {
log.info("**Upload File Controller!");
FileCriteria criteria = new FileCriteria();
criteria.setFileId(fileId);
try {
//upload file
Iterator<String> itr = multiPartRequest.getFileNames();
MultipartFile mpf = null;
while(itr.hasNext()){
mpf = multiPartRequest.getFile(itr.next());
break;
}
byte[] fileByteArr = null;
if (null != mpf && !mpf.isEmpty()) {
String originalFileName = mpf.getOriginalFilename();
log.info(originalFileName);
criteria.setFileName("");
String fileExtension = FilenameUtils.getExtension(originalFileName);
criteria.setFileExtension(fileExtension);
fileByteArr = mpf.getBytes();
criteria.setFileByteArray(fileByteArr);
criteria.setFileName(originalFileName);
}
} catch (IOException e) {
e.printStackTrace();
log.error(e.getMessage());
}
return fileService.uploadFile(criteria);
}
}
- 后端業(yè)務(wù)邏輯代碼,也就是文件上傳處理邏輯Service代碼如下:
(1)業(yè)務(wù)接口:
public interface FileService {
StandardResponse uploadFile(FileCriteria criteria);
String saveFile(FileCriteria criteria);
}
(2)業(yè)務(wù)實(shí)現(xiàn)類:
@Service
@Slf4j
public class FileServiceImpl implements FileService {
@Value("${project.root.path}")
private String rootPath = "rootPath";
@Value("${project.baseUrl}")
private String baseUrl;
@Override
public StandardResponse uploadFile(FileCriteria criteria) {
String filePath = this.saveFile(criteria);
String imgPath = baseUrl + "filePath/" + filePath;
StandardResponse standardResponse = new StandardResponse();
standardResponse.setSuccess(true);
standardResponse.setStatusCode("100");
standardResponse.setStatusDesc("上傳成功");
standardResponse.setData(imgPath);
return standardResponse;
}
@Override
public String saveFile(FileCriteria criteria) {
log.info("上傳文件開始!");
String pictureId = IdUtils.getId("FP");
String fileName = pictureId + "." + criteria.getFileExtension();
criteria.setFileName(fileName);
String filePath = sourceFile(criteria);
log.info("File Path: " + filePath);
log.info("上傳文件結(jié)束!");
return filePath;
}
private String sourceFile(FileCriteria criteria) {
byte[] attachmentFileByteArray = criteria.getFileByteArray();
if (null != attachmentFileByteArray) {
log.info("1.1.創(chuàng)建根目錄.");
String basePath = rootPath + this.genDatePath();
File basePathFolder = new File(basePath);
if (!basePathFolder.exists()) basePathFolder.mkdirs();
log.info("根目錄: " + basePath);
File file = new File(basePath + File.separator + criteria.getFileName());
log.info("1.2.保存源文件 - 絕對路徑: " + file.getAbsolutePath());
try {
FileCopyUtils.copy(attachmentFileByteArray, file);
log.info("1.3.1.保存源文件 - 保存成功 !!!");
String relativePath = this.genDatePath() + File.separator + criteria.getFileName();
return relativePath;
} catch (IOException e) {
log.info("1.3.2.保存源文件 - 保存失敗 !!!");
file.deleteOnExit();
return "";
}
}
return "";
}
private String genDatePath(){
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
String yyyyMMdd = sdf.format(new Date());
return yyyyMMdd;
}
}
- 配置文件
server:
port: 8740
project:
root:
path: /var/tomcat/file-manage/filePath/
baseUrl: http://111.114.238.134:8740/
- 訪問域名或IP加端口訪問到Thymeleaf頁面,要添加一個Controller跳轉(zhuǎn)
@Controller
public class IndexController {
@GetMapping("/")
public String Index() {
return "index";
}
}
- 在templates目錄下創(chuàng)建index.htm頁面文件,這里的index名要和上面Controller返回“index”名一致,才能跳轉(zhuǎn)過去,index.html代碼如下:
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>文件上傳</title>
</head>
<body>
<p>單文件上傳</p>
<form method="post" action="/file/upload" enctype="multipart/form-data">
<input type="text" name="fileId" value="FP00001">
<p><input type="file" name="file00"></p>
<input type="submit" value="提交">
</form>
</body>
</html>
上面圖片就是Thymeleaf頁面,上傳文件成功后效果。
總結(jié)
通過此貼學(xué)習(xí)到文件上傳3.0與3.1的不同處,同時也學(xué)習(xí)到了后端開發(fā)流程,其實(shí)寫這個貼子之前,是一個小伙伴問到我關(guān)于文件上傳問題,由于之前我寫的實(shí)例里,也沒有用到文件上傳功能,于是我就用最新API9也就是Stage模式寫了一個Demo給他參考,然后他通過參考我的Demo,學(xué)會了,可惜他現(xiàn)在開發(fā)的項目是用API8的,由于開發(fā)模式不一樣,他遇到了問題,于是我在用API8寫了一個Demo給他參考,最后他的項目也實(shí)現(xiàn)了文件上傳。
文章相關(guān)附件可以點(diǎn)擊下面的原文鏈接前往下載:
https://ost.51cto.com/resource/2729。
https://ost.51cto.com/resource/2730。
https://ost.51cto.com/resource/2731。