鸿蒙系统开发【网络-上传和下载(ArkTS)】基本功能

news2024/11/14 20:50:30

网络-上传和下载(ArkTS)

介绍

本示例使用@ohos.request接口创建上传和下载任务,实现上传、下载功能,hfs作为服务器,实现了文件的上传和下载和任务的查询功能。

效果预览

1

使用说明

1.本示例功能需要先配置服务器环境后使用,具体配置见[上传下载服务配置]。

2.首页展示上传和下载两个入口组件,点击进入对应的页面,如果要使用后台下载任务,请开启后台任务开关。

3.上传页面(请先在图库中确定已开启图库权限):

​ 点击**+**,从相册选择拉起图库选择照片,图片选择页面支持拍照,选择照片后点击发表进行上传。

​ 在首页中打开后台任务开关后,上传页面开启的是后台上传任务,后台任务在应用退出到后台时可以在通知栏看到任务状态。

4.下载页面:

​ 点击文件列表选择要下载的文件后,点击下载选择指定路径后开始下载。

​ 点击查看下载文件进入下载文件页面,点击文件夹查看文件夹内的文件。

​ 在首页中打开后台任务开关后,下载页面开启的是后台下载任务,后台任务在应用退出到后台时可以在通知栏看到任务状态。

​ 前台下载时只支持单文件下载,后台下载时支持选择多个文件下载。

具体实现

  • 该示例分为两个模块:

    • 上传模块

      • 使用@ohos.request中接口agent.create创建上传任务,调用@ohos.request中的Task相关接口实现上传任务的创建、取消、进度加载,失败的任务会调用查询接口获取失败原因并打印在日志中,支持多个文件上传。
  • 源码参考:[RequestUpload.ets]

/*
 * 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 { common } from '@kit.AbilityKit';
import { promptAction } from '@kit.ArkUI';
import { request } from '@kit.BasicServicesKit';
import { urlUtils } from '../utils/UrlUtils';
import { logger } from '../utils/Logger';
import { MediaUtils } from '../utils/MediaUtils';
import { BackgroundTaskState, UPLOAD_TOKEN, TOAST_BOTTOM, TASK_MAX } from '../utils/Constants';

const TAG: string = 'RequestUpload';
const HEADER: Record<string, string> = { 'Content-Type': 'multipart/form-data' };

class Upload {
  private mediaUtils: MediaUtils = new MediaUtils();
  private config: request.agent.Config = {
    action: request.agent.Action.UPLOAD,
    headers: HEADER,
    url: '',
    mode: request.agent.Mode.FOREGROUND,
    method: 'POST',
    title: 'upload',
    network: request.agent.Network.ANY,
    data: [],
    token: UPLOAD_TOKEN
  }
  private context: common.UIAbilityContext | undefined = undefined;
  private uploadTask: request.agent.Task | undefined = undefined;
  private backgroundTask: request.agent.Task | undefined = undefined;
  private waitList: Array<string> = [];
  progressCallback: Function | undefined = undefined;
  completedCallback: Function | undefined = undefined;
  failedCallback: Function | undefined = undefined;

  constructor() {
    setInterval(() => {
      this.flushBackgroundTask()
    }, 2000);
  }

  async uploadFilesBackground(fileUris: Array<string>): Promise<void> {
    logger.info(TAG, `uploadFiles begin, ${JSON.stringify(fileUris)}`);
    this.context = getContext(this) as common.UIAbilityContext;
    if (fileUris.length === 0) {
      return;
    }
    fileUris.forEach((item: string) => {
      this.waitList.push(item);
    });
  }

  async flushBackgroundTask() {
    let tasks = await request.agent.search({
      state: request.agent.State.RUNNING
    });
    let state = AppStorage.get<number>('backTaskState');
    if (state === BackgroundTaskState.RUNNING) {
      if (tasks.length < TASK_MAX && this.waitList.length > 0) {
        this.createBackgroundTask(this.waitList);
        this.waitList = [];
      } else {
        if (this.backgroundTask === undefined || tasks.indexOf(this.backgroundTask.tid) === -1) {
          AppStorage.setOrCreate('backTaskState', BackgroundTaskState.NONE);
          this.backgroundTask = undefined;
        }
      }
    }
  }

  async createBackgroundTask(fileUris: Array<string>) {
    if (this.context === undefined) {
      return;
    }
    this.config.url = await urlUtils.getUrl(this.context);
    this.config.data = await this.getFilesAndData(this.context.cacheDir, fileUris);
    this.config.mode = request.agent.Mode.BACKGROUND;
    try {
      this.backgroundTask = await request.agent.create(this.context, this.config);
      await this.backgroundTask.start();
      let state = AppStorage.get<number>('backTaskState');
      if (state === BackgroundTaskState.PAUSE) {
        await this.backgroundTask.pause();
      }
      logger.info(TAG, `createBackgroundTask success`);
    } catch (err) {
      logger.error(TAG, `task  err, err  = ${JSON.stringify(err)}`);
    }
  }

  async uploadFiles(fileUris: Array<string>, callback: (progress: number, isSucceed: boolean) => void): Promise<void> {
    logger.info(TAG, `uploadFiles begin, ${JSON.stringify(fileUris)}`);
    if (fileUris.length === 0) {
      return;
    }
    // 查询到存在正在执行的上传任务,提示并返回
    let tasks = await request.agent.search({
      state: request.agent.State.RUNNING,
      action: request.agent.Action.UPLOAD,
      mode: request.agent.Mode.FOREGROUND
    });
    if (tasks.length > 0) {
      promptAction.showToast({ message: $r('app.string.have_upload_task_tips'), bottom: TOAST_BOTTOM });
      return;
    }
    let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
    this.config.data = await this.getFilesAndData(context.cacheDir, fileUris);
    this.config.url = await urlUtils.getUrl(context);
    this.config.mode = request.agent.Mode.FOREGROUND;
    try {
      this.uploadTask = await request.agent.create(context, this.config);
      this.uploadTask.on('progress', this.progressCallback = (progress: request.agent.Progress) => {
        logger.info(TAG, `progress,  progress = ${progress.processed} ${progress.state}`);
        let processed = Number(progress.processed.toString()).valueOf();
        let size = progress.sizes[0];
        let process: number = Math.floor(processed / size * 100);
        if (process < 100) {
          callback(process, false);
        }
      });
      this.uploadTask.on('completed', this.completedCallback = (progress: request.agent.Progress) => {
        logger.info(TAG, `complete,  progress = ${progress.processed} ${progress.state}`);
        callback(100, true);
        this.cancelTask();
      });
      this.uploadTask.on('failed', this.failedCallback = async (progress: request.agent.Progress) => {
        if (this.uploadTask) {
          let taskInfo = await request.agent.touch(this.uploadTask.tid, UPLOAD_TOKEN);
          logger.info(TAG, `fail,  resean = ${taskInfo.reason}, faults = ${JSON.stringify(taskInfo.faults)}`);
        }
        callback(100, false);
        this.cancelTask();
      });
      await this.uploadTask.start();
    } catch (err) {
      logger.error(TAG, `task  err, err  = ${JSON.stringify(err)}`);
      callback(100, false);
    }
  }

  async cancelTask() {
    if (this.uploadTask === undefined) {
      return;
    }
    try {
      this.uploadTask.off('progress');
      this.progressCallback = undefined;
      this.uploadTask.off('failed');
      this.failedCallback = undefined
      this.uploadTask.off('completed');
      this.completedCallback = undefined
      await this.uploadTask.stop();
      await request.agent.remove(this.uploadTask.tid);
    } catch (err) {
      logger.info(TAG, `deleteTask fail,err= ${JSON.stringify(err)}`);
    }
    this.uploadTask = undefined;
  }

  async pauseOrResume() {
    let state = AppStorage.get<number>('backTaskState');
    if (state === BackgroundTaskState.RUNNING) {
      await this.pause();
      AppStorage.setOrCreate('backTaskState', BackgroundTaskState.PAUSE);
    } else if (state === BackgroundTaskState.PAUSE) {
      await this.resume();
      AppStorage.setOrCreate('backTaskState', BackgroundTaskState.RUNNING);
    } else {
      logger.info(TAG, 'this task state is error');
    }
  }

  async pause() {
    logger.info(TAG, 'pause');
    if (this.backgroundTask === undefined) {
      return;
    }
    try {
      await this.backgroundTask.pause();
    } catch (err) {
      logger.info(TAG, `pause fail,err= ${JSON.stringify(err)}`);
    }
  }

  async resume() {
    logger.info(TAG, 'resume');
    if (this.backgroundTask === undefined) {
      return;
    }
    try {
      await this.backgroundTask.resume();
    } catch (err) {
      logger.info(TAG, `resume fail,err= ${JSON.stringify(err)}`);
    }
  }

  private async getFilesAndData(cacheDir: string, fileUris: Array<string>): Promise<Array<request.agent.FormItem>> {
    logger.info(TAG, `getFilesAndData begin`);
    let files: Array<request.agent.FormItem> = [];
    for (let i = 0; i < fileUris.length; i++) {
      logger.info(TAG, `getFile fileUri = ${fileUris[i]}`);
      let imagePath = await this.mediaUtils.copyFileToCache(cacheDir, fileUris[i]);
      logger.info(TAG, `getFilesAndData ${JSON.stringify(imagePath)}`);
      let file: request.agent.FormItem = {
        name: imagePath.split('cache/')[1],
        value: {
          path: './' + imagePath.split('cache/')[1]
        }
      }
      files.push(file);
    }
    logger.info(TAG, `getFilesAndData ${JSON.stringify(files)}`);
    return files;
  }
}

export const requestUpload = new Upload();
  • 源码参考:[AddPictures.ets]
/*
 * 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 { picker } from '@kit.CoreFileKit';
import { logger } from '@ohos/uploaddownload';
import { BusinessError } from '@kit.BasicServicesKit';

const TAG: string = 'AddPictures';

@Extend(Image) function imageStyle() {
  .width('100%')
  .aspectRatio(1)
  .objectFit(ImageFit.Fill)
  .backgroundColor($r('app.color.light_gray'))
  .borderRadius(12)
}

const TEXT_WIDTH_FULL: Length = '100%';
@Component
export struct AddPictures {
  @Consume imageList: Array<string>;

  build() {
    Column() {
      Text($r('app.string.tip'))
        .fontColor($r('app.color.text_normal'))
        .fontWeight(400)
        .fontFamily('HarmonyHeiTi')
        .fontSize(14)
        .opacity(0.4)
        .margin({ top: $r('app.float.add_pictures_margin_top'), bottom: $r('app.float.add_pictures_margin_bottom') })
        .width(TEXT_WIDTH_FULL)
      GridRow({ columns: { sm: 3, md: 6, lg: 8 }, gutter: 12 }) {
        ForEach(this.imageList, (item: string) => {
          GridCol({ span: 1 }) {
            Image(item)
              .imageStyle()
          }
        })
        GridCol({ span: 1 }) {
          Row() {
            Image($r('app.media.ic_public_add'))
              .size({ width: 24, height: 24 })
              .objectFit(ImageFit.Contain)
          }
          .width('100%')
          .height('100%')
          .justifyContent(FlexAlign.Center)
        }
        .width('100%')
        .aspectRatio(1)
        .backgroundColor($r('app.color.white'))
        .borderRadius(12)
        .onClick(() => {
          this.showDialog();
        })
      }
    }
    .width('100%')
  }

  addImages = (images: Array<string>) => {
    images.forEach((item: string) => {
      if (!this.imageList.includes(item)) {
        this.imageList.push(item);
      }
    })
    logger.info(TAG, `addImages imageList=${JSON.stringify(this.imageList)}`);
  }

  showDialog() {
    AlertDialog.show({
      message: $r('app.string.pick_album'),
      alignment: DialogAlignment.Bottom,
      offset: { dx: 0, dy: -12 },
      primaryButton: {
        value: $r('app.string.cancel'),
        fontColor: $r('app.color.btn_text_blue'),
        action: () => {
        }
      },
      secondaryButton: {
        value: $r('app.string.ok'),
        fontColor: $r('app.color.btn_text_blue'),
        action: () => {
          try {
            let photoSelectOptions = new picker.PhotoSelectOptions();
            photoSelectOptions.MIMEType = picker.PhotoViewMIMETypes.IMAGE_TYPE;
            photoSelectOptions.maxSelectNumber = 5;
            let photoPicker = new picker.PhotoViewPicker();
            photoPicker.select(photoSelectOptions).then((photoSelectResult: picker.PhotoSelectResult) => {
              this.addImages(photoSelectResult.photoUris);
            }).catch((err: BusinessError) => {
              logger.error(TAG, `'PhotoViewPicker.select failed with err: ${JSON.stringify(err)}`);
            });
          } catch (err) {
            logger.error(TAG, `'PhotoViewPicker failed with err: ${JSON.stringify(err)}`);
          }
        }
      }
    })
  }
}
  • 源码参考:[Upload.ets]
/*
 * 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 { common } from '@kit.AbilityKit';
import { promptAction } from '@kit.ArkUI';
import { AddPictures } from '../components/AddPictures';
import { BackgroundTaskState, requestUpload, TOAST_BOTTOM } from '@ohos/uploaddownload';

const TIME_MAX: number = 5;

@Entry
@Component
struct Upload {
  @StorageLink('isBackground') isBackground: boolean = false;
  @StorageLink('backTaskState') @Watch('stateChange') backTaskState: BackgroundTaskState = BackgroundTaskState.NONE;
  @State isBegin: boolean = false;
  @Provide imageList: Array<string> = [];
  @State progress: number = 0;
  @State countdown: number = 0;

  build() {
    Navigation() {
      Scroll() {
        AddPictures()
      }
      .padding({ left: 24, right: 24 })
      .width('100%')
      .layoutWeight(1)
      .align(Alignment.Top)

      Column() {
        Button() {
          if (this.isBackground && this.backTaskState !== BackgroundTaskState.NONE) {
            if (this.backTaskState === BackgroundTaskState.RUNNING) {
              Text($r('app.string.pause'))
                .fontSize(16)
                .fontWeight(500)
                .fontColor($r('app.color.white'))
            } else {
              Text($r('app.string.continue'))
                .fontSize(16)
                .fontWeight(500)
                .fontColor($r('app.color.white'))
            }
          } else if (this.isBegin && !this.isBackground) {
            Row() {
              Progress({ value: this.progress, type: ProgressType.Ring })
                .width(20)
                .height(20)
                .backgroundColor('#FFFFFF')
                .color('#558DFF')
                .style({ strokeWidth: 2, scaleCount: 100, scaleWidth: 2 })
              Text(`${this.getResourceString($r('app.string.uploading'))}${this.progress}%`)
                .fontSize(16)
                .fontColor('#FFFFFF')
                .fontWeight(500)
                .margin({ left: 12 })
            }.alignItems(VerticalAlign.Center)
          } else {
            if (this.countdown > 0) {
              Text(`${this.countdown}s`)
                .fontSize(16)
                .fontWeight(500)
                .fontColor($r('app.color.white'))
            } else {
              Text($r('app.string.upload'))
                .fontSize(16)
                .fontWeight(500)
                .fontColor($r('app.color.white'))
            }
          }
        }
        .id('publish')
        .width('100%')
        .height(40)
        .margin({ bottom: this.isBegin ? 16 : 24 })
        .enabled(this.countdown > 0 ? false : true)
        .backgroundColor($r('app.color.button_blue'))
        .onClick(() => {
          if (this.isBackground && this.backTaskState !== BackgroundTaskState.NONE) {
            requestUpload.pauseOrResume();
          } else {
            this.uploadFiles();
          }
        })

        if (this.isBegin) {
          Button() {
            Text($r('app.string.cancel'))
              .fontSize(16)
              .fontWeight(500)
              .fontColor($r('app.color.btn_text_blue'))
          }
          .id('cancel')
          .width('100%')
          .height(40)
          .margin({ bottom: 24 })
          .backgroundColor($r('app.color.button_light_gray'))
          .onClick(() => {
            // cancel task
            requestUpload.cancelTask();
            this.progress = 0;
            this.isBegin = false;
          })
        }
      }
      .width('100%')
      .padding({ left: 24, right: 24 })
    }
    .width('100%')
    .height('100%')
    .backgroundColor($r('app.color.light_gray'))
    .title($r('app.string.upload'))
    .hideBackButton(false)
    .titleMode(NavigationTitleMode.Mini)
    .mode(NavigationMode.Stack)
  }

  aboutToAppear() {
    this.isBegin = false;
    this.backTaskState = BackgroundTaskState.NONE;
  }

  stateChange() {
    if (this.backTaskState === BackgroundTaskState.NONE) {
      this.imageList = [];
    }
  }

  uploadFiles() {
    if (this.imageList.length === 0) {
      return;
    }
    if (this.isBackground) {
      AppStorage.setOrCreate('backTaskState', BackgroundTaskState.RUNNING)
      requestUpload.uploadFilesBackground(this.imageList);
      promptAction.showToast({ message: $r('app.string.background_task_start'), bottom: TOAST_BOTTOM });
    } else {
      this.isBegin = true;
      this.progress = 0;
      requestUpload.uploadFiles(this.imageList, (progress: number, isSucceed: boolean) => {
        this.progress = progress;
        if (this.progress === 100 && isSucceed) {
          this.isBegin = false;
          this.imageList = [];
          promptAction.showToast({ message: $r('app.string.upload_success'), bottom: TOAST_BOTTOM })
        }
        if (this.progress === 100 && isSucceed === false) {
          this.isBegin = false;
          this.countdown = TIME_MAX;
          let interval = setInterval(() => {
            if (this.countdown > 0) {
              this.countdown--;
            } else {
              clearInterval(interval);
            }
          }, 1000);
          promptAction.showToast({ message: $r('app.string.upload_fail'), bottom: TOAST_BOTTOM })
        }
      });
    }
  }

  getResourceString(resource: Resource) {
    let context = getContext(this) as common.UIAbilityContext;
    return context.resourceManager.getStringSync(resource.id);
  }
}
  • 参考接口:@ohos.request,@ohos.file.picker

    • 下载模块

      • 使用@ohos.request中接口agent.create创建上传任务,调用@ohos.request中的Task相关接口实现上传任务的创建、取消、暂停、继续、进度加载,失败的任务会调用查询接口获取失败原因并打印在日志中,前台下载任务只支持单个文件下载,后台下载任务支持多文件下载。使用@ohos.file.fs完成指定路径的创建和查询已下载的文件。
  • 源码参考:[RequestDownload.ets]

/*
 * 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 { common } from '@kit.AbilityKit';
import { promptAction } from '@kit.ArkUI';
import { request } from '@kit.BasicServicesKit';
import { logger } from '../utils/Logger';
import { TOAST_BOTTOM, TASK_MAX, TASK_PAUSE_MSG, TASK_NET_PAUSE_MSG, TASK_RESUME_MSG, TASK_NET_RESUME_MSG } from '../utils/Constants';

const TAG: string = 'RequestDownload';
let isNetPause = false;
class RequestDownload {
  private context: common.UIAbilityContext | undefined = undefined;
  private waitList: Array<string[]> = [];
  private downloadTask: request.agent.Task | undefined = undefined;
  progressCallback: Function | undefined = undefined;
  completedCallback: Function | undefined = undefined;
  failedCallback: Function | undefined = undefined;

  constructor() {
    setInterval(() => {
      this.flushBackgroundTask()
    }, 2000);
  }

  async downloadFilesBackground(folder: string, files: Array<string>) {
    logger.info(TAG, 'downloadFiles');
    this.context = getContext(this) as common.UIAbilityContext;
    files.forEach((item: string) => {
      this.waitList.push([folder, item]);
    });
  }

  async flushBackgroundTask() {
    let tasks = await request.agent.search({
      state: request.agent.State.RUNNING
    });
    if (tasks.length < TASK_MAX && this.waitList.length > 0) {
      let downloadList: Array<string[]> = [];
      if (this.waitList.length <= TASK_MAX - tasks.length) {
        downloadList = this.waitList;
        this.waitList = [];
      } else {
        downloadList = this.waitList.slice(0, TASK_MAX - tasks.length);
        this.waitList = this.waitList.slice(TASK_MAX - tasks.length, this.waitList.length);
      }
      logger.info(TAG, `this.waitList = ${JSON.stringify(this.waitList)}`);
      this.createBackgroundTask(downloadList);
    }
  }

  async createBackgroundTask(downloadList: Array<string[]>) {
    if (this.context === undefined) {
      return;
    }
    for (let i = 0; i < downloadList.length; i++) {
      try {
        let splitUrl = downloadList[i][1].split('//')[1].split('/');
        let downloadConfig: request.agent.Config = {
          action: request.agent.Action.DOWNLOAD,
          url: downloadList[i][1],
          method: 'POST',
          title: 'download',
          mode: request.agent.Mode.BACKGROUND,
          network: request.agent.Network.ANY,
          saveas: `./${downloadList[i][0]}/${splitUrl[splitUrl.length-1]}`,
          overwrite: true,
          gauge: true
        }
        let downTask = await request.agent.create(this.context, downloadConfig);
        await downTask.start();
      } catch (err) {
        logger.error(TAG, `task  err, err  = ${JSON.stringify(err)}`);
        this.waitList.push(downloadList[i]);
      }
    }
  }

  async pause() {
    if (this.downloadTask) {
      let taskInfo = await request.agent.show(this.downloadTask.tid);
      logger.info(TAG, `task pause, taskInfo  = ${JSON.stringify(taskInfo)}`);
      await this.downloadTask.pause();
    }
  }

  async resume() {
    if (this.downloadTask) {
      let taskInfo = await request.agent.show(this.downloadTask.tid);
      logger.info(TAG, `task resume, taskInfo  = ${JSON.stringify(taskInfo)}`);
      await this.downloadTask.resume();
    }
  }

  async downloadFile(folder: string, url: string, callback: (progress: number, isSuccess: boolean) => void) {
    logger.info(TAG, 'downloadFile');
    // 查询到存在正在执行的下载任务,提示并返回
    let tasks = await request.agent.search({
      state: request.agent.State.RUNNING,
      action: request.agent.Action.DOWNLOAD,
      mode: request.agent.Mode.FOREGROUND
    });
    if (tasks.length > 0) {
      promptAction.showToast({ message: $r('app.string.have_download_task_tips'), bottom: TOAST_BOTTOM });
      return;
    }
    let splitUrl = url.split('//')[1].split('/');
    let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
    let downloadConfig: request.agent.Config = {
      action: request.agent.Action.DOWNLOAD,
      url: url,
      method: 'GET',
      title: 'download',
      mode: request.agent.Mode.BACKGROUND,
      retry: true,
      network: request.agent.Network.ANY,
      saveas: `./${folder}/${splitUrl[splitUrl.length-1]}`,
      overwrite: true
    }
    logger.info(TAG, `downloadFile, downloadConfig = ${JSON.stringify(downloadConfig)}`);
    try {
      this.downloadTask = await request.agent.create(context, downloadConfig);
      this.downloadTask.on('progress', this.progressCallback = (progress: request.agent.Progress) => {
        logger.info(TAG, `progress,  progress = ${progress.processed} ${progress.state}`);
        let processed = Number(progress.processed.toString()).valueOf();
        let size = progress.sizes[0];
        let process: number = Math.floor(processed / size * 100);
        if (process < 100) {
          callback(process, false);
        }
      })
      this.downloadTask.on('completed', this.completedCallback = (progress: request.agent.Progress) => {
        logger.info(TAG, `download complete, file= ${url}, progress = ${progress.processed}`);
        callback(100, true);
        this.deleteTask();
      })
      this.downloadTask.on('pause', this.failedCallback = async (progress: request.agent.Progress) => {
        if (this.downloadTask) {
          let taskInfo = await request.agent.show(this.downloadTask.tid);
          logger.info(TAG, `pause,  resean = ${taskInfo.reason}, progress = ${progress.processed}, faults = ${JSON.stringify(taskInfo.faults)}`);
          isNetPause = taskInfo.faults === 0;
          if (isNetPause) {
            callback(TASK_NET_PAUSE_MSG, isNetPause);
          }
          else {
            callback(TASK_PAUSE_MSG, isNetPause);
          }
        }
      })
      this.downloadTask.on('resume', this.failedCallback = async (progress: request.agent.Progress) => {
        if (this.downloadTask) {
          let taskInfo = await request.agent.show(this.downloadTask.tid);
          logger.info(TAG, `resume,  resean = ${taskInfo.reason}, progress = ${progress.processed}, faults = ${JSON.stringify(taskInfo.faults)}`);
          if (isNetPause) {
            isNetPause = false;
            callback(TASK_NET_RESUME_MSG, isNetPause);
          }
          else {
            callback(TASK_RESUME_MSG, isNetPause);
          }
        }
      })
      this.downloadTask.on('failed', this.failedCallback = async (progress: request.agent.Progress) => {
        if (this.downloadTask) {
          let taskInfo = await request.agent.show(this.downloadTask.tid);
          logger.info(TAG, `fail,  resean = ${taskInfo.reason}, progress = ${progress.processed}, faults = ${JSON.stringify(taskInfo.faults)}`);
        }
        callback(100, false);
        this.deleteTask();
      })
      await this.downloadTask.start();
    } catch (err) {
      logger.error(TAG, `task  err, err  = ${JSON.stringify(err)}`);
      callback(100, false);
    }
  }

  async deleteTask() {
    if (this.downloadTask) {
      try {
        this.downloadTask.off('progress');
        this.progressCallback = undefined;
        this.downloadTask.off('completed');
        this.completedCallback = undefined
        this.downloadTask.off('failed');
        this.failedCallback = undefined
        await request.agent.remove(this.downloadTask.tid);
      } catch (err) {
        logger.info(TAG, `deleteTask fail, err= ${JSON.stringify(err)}`);
      }
    }

    this.downloadTask = undefined;
  }
}

export const requestDownload = new RequestDownload();
  • 源码参考:[Download.ets]
/*
 * 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 { promptAction } from '@kit.ArkUI';
import { router } from '@kit.ArkUI';
import { CustomDataSource } from '../components/CustomDataSource';
import {
  FileModel,
  FileType,
  fileUtils,
  logger,
  requestFiles,
  requestDownload,
  TOAST_BOTTOM,
  TASK_PAUSE_MSG,
  TASK_RESUME_MSG,
  TASK_NET_PAUSE_MSG,
  TASK_NET_RESUME_MSG
} from '@ohos/uploaddownload';
import { SelectFolderDialog } from '../components/SelectFolderDialog';

const TAG: string = 'Download';

const OFFSET_DY: Length = -12;
const OFFSET_DX: Length = 0;
const LOADING_PROGRESS_WIDTH: Length = 100;
const FULL_WIDTH: Length = '100%';
const FULL_HEIGHT: Length = '100%';
const LIST_WIDTH: Length = '100%';
const LIST_HEIGHT: Length = 'auto';
const LIST_BORDER_RADIUS: Length | BorderRadiuses = 24;
const PADDING_TOP: Length = 4;
const PADDING_BOTTOM: Length = 4;
const STROKE_WIDTH: Length = 1;
const START_MARGIN: Length = 44;
const END_MARGIN: Length = 12;
const COLUMN_PADDING_LEFT: Length = 12;
const COLUMN_PADDING_RIGHT: Length = 12;
const COLUMN_PADDING_BOTTOM: Length = 12;
const BUTTON_FONT_SIZE = 16;
const MARGIN_TOP: Length = 12;
const MARGIN_LEFT: Length = 12;
const MARGIN_RIGHT: Length = 12;
const MARGIN_BOTTOM: Length = 12;
const BUTTON_HEIGHT: Length = 45;
@Entry
@Component
struct Download {
  private fileData: CustomDataSource = new CustomDataSource([]);
  @StorageLink('isBackground') isBackground: boolean = false;
  @Provide downloadFolder: Array<string> = [];
  @State isGetData: boolean = false;
  @State checkFile: Array<string> = [];
  @State checkList: Array<boolean> = [];
  @State isRunning: boolean = false;
  @State isPause: boolean = false;
  @State isNetPause: boolean = false;
  @State progress: number = 0;
  private selectFolder = (folder: string) => {
    logger.info(TAG, `selectFolder = ${folder}`);
    this.download(folder);
  }
  private folderDialogController: CustomDialogController = new CustomDialogController({
    builder: SelectFolderDialog({ selectFolder: this.selectFolder }),
    autoCancel: true,
    alignment: DialogAlignment.Bottom,
    offset: { dx: OFFSET_DX,
      dy: OFFSET_DY }
  });

  build() {
    Navigation() {
      Column() {
        if (this.isGetData) {
          LoadingProgress()
            .width(LOADING_PROGRESS_WIDTH)
            .layoutWeight(1)
        } else {
          List({ space: 12 }) {
            LazyForEach(this.fileData, (item: FileModel, index: number) => {
              ListItem() {
                this.FileItem(item, index)
              }
            }, (item: FileModel) => JSON.stringify(item))
          }
          .width(LIST_WIDTH)
          .height(LIST_HEIGHT)
          .scrollBar(BarState.Off)
          .backgroundColor(Color.White)
          .borderRadius(LIST_BORDER_RADIUS)
          .padding({ top: PADDING_TOP,
            bottom: PADDING_BOTTOM })
          .divider({ strokeWidth: STROKE_WIDTH,
            startMargin: START_MARGIN,
            endMargin: END_MARGIN })
        }
        Column().layoutWeight(1)

        this.BottomView()
      }
      .padding({ left: COLUMN_PADDING_LEFT,
        right: COLUMN_PADDING_RIGHT,
        bottom: COLUMN_PADDING_BOTTOM })
      .height(FULL_HEIGHT)
    }
    .width(FULL_WIDTH)
    .height(FULL_HEIGHT)
    .hideBackButton(false)
    .titleMode(NavigationTitleMode.Mini)
    .mode(NavigationMode.Stack)
    .backgroundColor($r('app.color.light_gray'))
    .hideToolBar(false)
    .title($r('app.string.download'))
  }

  @Builder
  FileItem(file: FileModel, index: number) {
    Row() {
      Row() {
        if (file.fileType === FileType.FOLDER) {
          Image($r('app.media.ic_files_folder'))
            .size({ width: 24, height: 24 })
            .objectFit(ImageFit.Contain)
        } else if (file.fileType === FileType.IMAGE) {
          Image($r('app.media.ic_public_picture'))
            .size({ width: 24, height: 24 })
            .objectFit(ImageFit.Contain)
        } else if (file.fileType === FileType.MUSIC) {
          Image($r('app.media.ic_public_music'))
            .size({ width: 24, height: 24 })
            .objectFit(ImageFit.Contain)
        } else if (file.fileType === FileType.Video) {
          Image($r('app.media.ic_public_video'))
            .size({ width: 24, height: 24 })
            .objectFit(ImageFit.Contain)
        } else {
          Image($r('app.media.ic_public_document'))
            .size({ width: 24, height: 24 })
            .objectFit(ImageFit.Contain)
        }

        Text(decodeURIComponent(file.name))
          .fontSize(16)
          .fontWeight(400)
          .layoutWeight(1)
          .maxLines(1)
          .textOverflow({ overflow: TextOverflow.Ellipsis })
          .margin({ left: 12 })
      }
      .layoutWeight(1)

      Checkbox({ name: '', group: 'checkboxGroup' })
        .select(this.checkList[index])
        .selectedColor($r('app.color.button_blue'))
        .margin({ left: 12 })
        .hitTestBehavior(HitTestMode.None)
    }
    .width('100%')
    .padding({ left: 12, right: 12 })
    .height(48)
    .onClick(() => {
      this.fileCheck(index);
    })
  }

  @Builder
  BottomView() {
    Column({ space: 12 }) {
      Button() {
        Row() {
          if (!this.isBackground && this.isRunning) {
            if (this.isPause || this.isNetPause) {
              Text($r('app.string.continue'))
                .fontColor(Color.White)
                .fontSize(BUTTON_FONT_SIZE)
            }
            else {
              Text(`${this.progress}%`)
                .fontColor(Color.White)
                .fontSize(BUTTON_FONT_SIZE)
              Text($r('app.string.downloading'))
                .fontColor(Color.White)
                .fontSize(BUTTON_FONT_SIZE)
                .margin({ left: MARGIN_LEFT })
            }
          } else {
            Text($r('app.string.download'))
              .fontColor(Color.White)
              .fontSize(BUTTON_FONT_SIZE)
          }
        }
      }
      .id('download_to')
      .type(ButtonType.Capsule)
      .height(BUTTON_HEIGHT)
      .width(FULL_WIDTH)
      .backgroundColor($r('app.color.button_blue'))
      .onClick(() => {
        if (!this.isRunning) {
          this.folderDialogController.open();
        }
        else {
          if (!this.isNetPause) {
            if (this.isPause) {
              requestDownload.resume();
            }
            else {
              requestDownload.pause();
            }
          }
        }
      })

      Button($r('app.string.view_download_files'))
        .id('view_download_files')
        .type(ButtonType.Capsule)
        .backgroundColor($r('sys.color.ohos_id_color_button_normal'))
        .width('100%')
        .fontSize(BUTTON_FONT_SIZE)
        .margin({ bottom: MARGIN_BOTTOM })
        .fontColor($r('app.color.btn_text_blue'))
        .onClick(() => {
          router.pushUrl({
            url: 'pages/DownloadFiles'
          });
        })
    }
    .margin({ top: MARGIN_TOP,
      left: MARGIN_LEFT,
      right: MARGIN_RIGHT })
  }

  aboutToAppear() {
    this.isRunning = false;
    this.isPause = false;
    this.isGetData = true;
    requestFiles.requestFiles().then((data: FileModel[]) => {
      this.checkList = [];
      this.isRunning = false;
      this.fileData.dataArray = data;
      this.fileData.dataArray.forEach(() => {
        this.checkList.push(false);
      })
      this.isGetData = false;
      this.fileData.notifyDataReload();
    })
    fileUtils.listFolders().then((folders: Array<string>) => {
      this.downloadFolder = folders;
    })
  }

  fileCheck(index: number) {
    if (!this.isBackground) {
      for (let i = 0; i < this.checkList.length; i++) {
        if (i !== index) {
          this.checkList[i] = false;
        }
      }
    }
    this.checkList[index] = !this.checkList[index];
    logger.info(TAG, `this.checkList = ${JSON.stringify(this.checkList)}`);
  }

  download(folder: string) {
    this.checkFile = [];
    if (this.checkList === undefined) {
      return;
    }
    logger.info(TAG, `this.checkList = ${JSON.stringify(this.checkList)}`);
    for (let i = 0; i < this.checkList.length; i++) {
      if (this.checkList[i]) {
        let fileModel = this.fileData.getData(i);
        logger.info(TAG, `fileModel = ${JSON.stringify(fileModel)}`);
        fileModel.files.forEach((url: string) => {
          let splitUrl = url.split('//')[1].split('/');
          if (splitUrl[splitUrl.length-1] !== '') {
            this.checkFile.push(url);
          }
        });
      }
    }
    logger.info(TAG, `this.checkFile = ${JSON.stringify(this.checkFile)}`);
    if (this.checkFile.length === 0) {
      promptAction.showToast({ message: $r('app.string.check_file_tips'), bottom: TOAST_BOTTOM });
      return;
    }
    this.progress = 0;
    if (this.isBackground) {
      this.isRunning = false;
      requestDownload.downloadFilesBackground(folder, this.checkFile);
      this.checkFile = [];
      this.checkList = [];
      this.fileData.dataArray.forEach(() => {
        this.checkList.push(false);
      })
      this.fileData.notifyDataReload();
      promptAction.showToast({ message: $r('app.string.background_task_start'), bottom: TOAST_BOTTOM });
    } else {
      this.isRunning = true;
      requestDownload.downloadFile(folder, this.checkFile[0], this.downloadFileCallback);
    }
  }

  downloadFilesCallback = (downloadCount: number, isSuccess: boolean) => {
    this.progress = downloadCount;
    if (downloadCount === this.checkFile.length) {
      this.downloadFinish(isSuccess);
    }
  }
  downloadFileCallback = (progress: number, isSuccess: boolean) => {
    logger.info(TAG, `downloadFileCallback = ${progress}`);
    if (progress === TASK_PAUSE_MSG) {
      this.isPause = true;
    }
    else if (progress === TASK_RESUME_MSG) {
      this.isPause = false;
    }
    else if (progress === TASK_NET_PAUSE_MSG) {
      this.isNetPause = true;
      let message = $r('app.string.net_pause');
      promptAction.showToast({ message: message, bottom: TOAST_BOTTOM });
    }
    else if (progress === TASK_NET_RESUME_MSG) {
      this.isNetPause = false;
      let message = $r('app.string.net_resume');
      promptAction.showToast({ message: message, bottom: TOAST_BOTTOM });
    }
    else {
      this.progress = progress;
      if (this.progress === 100) {
        this.downloadFinish(isSuccess);
      }
    }
  }

  downloadFinish(isSuccess: boolean) {
    this.isRunning = false;
    this.checkFile = [];
    this.checkList = [];
    this.fileData.dataArray.forEach(() => {
      this.checkList.push(false);
    })
    this.fileData.notifyDataReload();
    let message = isSuccess ? $r('app.string.download_finish') : $r('app.string.download_fail');
    promptAction.showToast({ message: message, bottom: TOAST_BOTTOM });
  }
}
  • 源码参考:[FileUtils.ets]
/*
 * 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 { common } from '@kit.AbilityKit';
import { fileIo } from '@kit.CoreFileKit';
import { logger } from '../utils/Logger';

const TAG: string = 'FileUtil';
const ALBUMS: string[] = ['Pictures', 'Videos', 'Others'];

class FileUtil {
  constructor() {
  }

  async initDownloadDir(): Promise<void> {
    let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
    logger.info(TAG, `initDownloadDir cacheDir=${context.cacheDir}`);
    try {
      fileIo.mkdirSync(`${context.cacheDir}/${ALBUMS[0]}`);
      fileIo.mkdirSync(`${context.cacheDir}/${ALBUMS[1]}`);
      fileIo.mkdirSync(`${context.cacheDir}/${ALBUMS[2]}`);
    } catch (err) {
      logger.info(TAG, `initDownloadDir err =${JSON.stringify(err)}`);
    }
  }

  async listFolders(): Promise<Array<string>> {
    await this.initDownloadDir();
    return ALBUMS;
  }

  async clearFolder(folderName: string): Promise<void> {
    let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
    try {
      let files: string[] = fileIo.listFileSync(`${context.cacheDir}/${folderName}`);
      logger.info(TAG, `listFiles listFileSync =${JSON.stringify(files)}`);
      for (let i = 0; i < files.length; i++) {
        fileIo.unlinkSync(`${context.cacheDir}/${folderName}/${files[i]}`);
      }
    } catch (err) {
      logger.info(TAG, `listFiles err =${JSON.stringify(err)}`);
    }
  }

  async listFiles(folderName: string): Promise<Array<string>> {
    let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
    let files: string[] = [];
    try {
      files = fileIo.listFileSync(`${context.cacheDir}/${folderName}`);
      logger.info(TAG, `listFiles listFileSync =${JSON.stringify(files)}`);
    } catch (err) {
      logger.info(TAG, `listFiles err =${JSON.stringify(err)}`);
    }
    return files;
  }
}

export const fileUtils = new FileUtil();
  • 源码参考:[FileBrowse.ets]
/*
 * 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 { fileUtils } from '../utils/FileUtils';

@Preview
@Component
export struct FileBrowse {
  @State folders: Array<string> = ['folder'];
  @State files: Array<string> = [];
  @State currentFolder: string = '';

  aboutToAppear() {
    fileUtils.listFolders().then((folders: Array<string>) => {
      this.folders = folders;
    })
  }

  build() {
    Navigation() {
      List({ space: 12 }) {
        ForEach(this.folders, (item: string) => {
          ListItem() {
            NavRouter() {
              Row() {
                Image($r('app.media.ic_files_folder'))
                  .size({ width: 32, height: 26 })
                  .objectFit(ImageFit.Contain)
                Text(item)
                  .fontSize(16)
                  .width('100%')
                  .margin({ left: 12 })
              }
              .height(56)
              .padding({ left: 16 })
              .backgroundColor(Color.White)
              .borderRadius(24)

              NavDestination() {
                this.FilesView()
              }
              .title(this.CustomTitle(item))
              .backgroundColor($r('app.color.light_gray'))
            }
            .onStateChange(async (isActivated: boolean) => {
              if (isActivated) {
                this.currentFolder = item;
                this.files = await fileUtils.listFiles(item);
              }
            })
          }
        })
      }
      .padding({ left: 12, right: 12 })
    }
    .hideBackButton(false)
    .titleMode(NavigationTitleMode.Mini)
    .title($r('app.string.download_files_title'))
    .mode(NavigationMode.Stack)
    .backgroundColor($r('app.color.light_gray'))
  }

  @Builder
  CustomTitle(title: string) {
    Row() {
      Text(title)
        .fontSize(20)
        .fontColor($r('app.color.text_normal'))
        .fontWeight(700)
        .margin({ left: 8 })
    }
    .width('100%')
  }

  @Builder
  FilesView() {
    Column() {
      List({ space: 12 }) {
        if (this.files.length === 0) {
          ListItem() {
            Text($r('app.string.folder_empty'))
              .fontSize(16)
              .width('100%')
              .margin({ top: 50 })
              .textAlign(TextAlign.Center)
          }
        }
        ForEach(this.files, (item: string) => {
          ListItem() {
            Text(decodeURIComponent(item))
              .fontSize(16)
              .width('100%')
          }
          .padding(12)
          .height(48)
          .backgroundColor(Color.White)
          .borderRadius(24)
        })
      }
      .padding({ left: 12, right: 12 })
      .layoutWeight(1)

      Column() {
        Button() {
          Image($r('app.media.ic_public_delete'))
            .objectFit(ImageFit.Cover)
            .size({ width: 24, height: 24 })
        }
        .type(ButtonType.Circle)
        .width(40)
        .height(40)
        .backgroundColor('#FF0000')
        .margin({ left: 5 })

        Text($r('app.string.clear_folder'))
          .fontSize(14)
          .fontColor($r('app.color.text_normal'))
          .opacity(0.6)
          .margin({ top: 8 })
      }
      .margin({ bottom: 24, top: 6 })
      .onClick(() => {
        fileUtils.clearFolder(this.currentFolder);
        this.files = [];
      })
    }
    .height('100%')
    .backgroundColor($r('app.color.light_gray'))
  }
}
  • 参考接口:@ohos.request,@ohos.file.fs

以上就是本篇文章所带来的鸿蒙开发中一小部分技术讲解;想要学习完整的鸿蒙全栈技术。可以在结尾找我可全部拿到!
下面是鸿蒙的完整学习路线,展示如下:
1

除此之外,根据这个学习鸿蒙全栈学习路线,也附带一整套完整的学习【文档+视频】,内容包含如下

内容包含了:(ArkTS、ArkUI、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、鸿蒙南向开发、鸿蒙项目实战)等技术知识点。帮助大家在学习鸿蒙路上快速成长!

鸿蒙【北向应用开发+南向系统层开发】文档

鸿蒙【基础+实战项目】视频

鸿蒙面经

在这里插入图片描述

为了避免大家在学习过程中产生更多的时间成本,对比我把以上内容全部放在了↓↓↓想要的可以自拿喔!谢谢大家观看!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1971807.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

BootStrap前端面试常见问题

在前端面试中&#xff0c;关于Bootstrap的问题通常围绕其基本概念、使用方式、特性以及实际应用等方面展开。以下是一些常见的问题及其详细解答&#xff1a; 1. Bootstrap是哪家公司研发的&#xff1f; 回答&#xff1a;Bootstrap是由Twitter的Mark Otto和Jacob Thornton合作…

go语言day21 goland使用gin框架、gorm框架操作mysql数据库redis数据库 使用宝塔创建redis数据库

GORM 指南 | GORM - The fantastic ORM library for Golang, aims to be developer friendly. gorm package - github.com/jinzhu/gorm - Go Packages go语言day20实现投票功能项目包-CSDN博客 gin框架标准项目结构&#xff1a; models&#xff1a;存放对应实体类和gorm包增删…

Godot的节点与场景

要深入的理解节点与场景&#xff0c;我们需要跳出这两个概念来看他。说的再直白一些godot本质就是一个场景编辑器&#xff01; 场景的概念应该在我们平时看电影看电视时会经常提到&#xff0c;比如某一个打斗的场景&#xff0c;这个场景可能会被设在某一个街道&#xff0c;那么…

RIP综合练习

要求&#xff1a; 1.合理使用IP地址划分网络&#xff0c;各自创建循环接口 2.R1创建环回172.16.1.1/24 172.16.2.1/24 172.16.3.1/24 3.要求R3使用R2访问R1环回 4.减少路由条目数量&#xff0c;R1,R2之间增加路由传递安全性 5.R5创建一个环回模拟运营商&#xff0c;不能…

Flink的DateStream API中的ProcessWindowFunction和AllWindowFunction两种用于窗口处理的函数接口的区别

目录 ProcessWindowFunction AllWindowFunction 具体区别 ProcessWindowFunction 示例 AllWindowFunction 示例 获取时间不同&#xff0c;一个数据产生的时间一个是数据处理的时间 ProcessWindowFunction AllWindowFunction 具体示例 ProcessWindowFunction 示例 Al…

CRMEB 电商系统安装及分析

CRMEB系统采用前后端分离技术&#xff0c;基于TP6Vue2.5Uniapp框架开发&#xff1b;支持微信小程序、公众号、H5、APP、PC端适配&#xff0c;数据同步&#xff01;是一套单商户新零售社交电商系统。 目录 安装 安装环境 安装过程 开始安装 安装检测 数据库配置 高级设置…

基于Cloudflare搭建私有Docker镜像源

周四原本不是发文的日子&#xff0c;主要因为两个原因&#xff1a; 第一个原因是总有人留言说 Docker 用不了&#xff0c;第二个原因是看了下上个月的阅读量&#xff0c;和之前比实在有点惨淡&#xff0c;除了文章总被人搬运外&#xff0c;我估计可能跟第一个原因多少还是有点…

计算机基础(Windows 10+Office 2016)教程 —— 第4章 计算机网络与Internet(上)

第4章 计算机网络与Internet 4.1 计算机网络概述4.1.1 计算机网络的定义4.1.2 计算机网络的发展4.1.3 计算机网络的功能4.1.4 计算机网络体系结构和TCP/IP 参考模型 4.2 计算机网络的组成和分类4.2.1 计算机网络的组成4.2.2 计算机网络的分类 4.3 网络传输介质和通信设备4.3.1 …

【Unity】3D功能开发入门系列(二)

Unity3D功能开发入门系列&#xff08;二&#xff09; 一、资源&#xff08;一&#xff09;资源文件&#xff08;二&#xff09;场景文件&#xff08;三&#xff09;资源包&#xff08;四&#xff09;Unity 资源商店&#xff08;五&#xff09;项目资源的导入 二、父子关系&…

【C语言篇】数据在内存中的存储(超详细)

文章目录 数据在内存中的存储二进制和进制转换基本概念进制转换十进制转二进制二进制转八进制和十六进制 整数在内存中的存储反码原码补码大小端字节序和字节序判断什么是大小端&#xff1f;为什么有大小端 浮点数在内存中的存储题目提出浮点数存的过程浮点数取的过程 题目解析…

Java语言程序设计——篇十一(2)

&#x1f33f;&#x1f33f;&#x1f33f;跟随博主脚步&#xff0c;从这里开始→博主主页&#x1f33f;&#x1f33f;&#x1f33f; 欢迎大家&#xff1a;这里是我的学习笔记、总结知识的地方&#xff0c;喜欢的话请三连&#xff0c;有问题可以私信&#x1f333;&#x1f333;&…

算法导论 总结索引 | 第五部分 第二十一章:用于不相交集合的数据结构

一些应用涉及 将n个不同的元素分成一组不相交的集合。寻找包含给定元素的唯一集合 和 合并两个集合 1、不相交集合的操作 1、一个不相交集合 数据结构 维持了 一个不相交动态集的集合 S {S_1, S_2,…, S_n}。用一个代表 来标识每个集合&#xff0c;它是这个集合的某个成员。…

IoTDB 入门教程 企业篇④——安全控制 | 白名单、审计日志、登录日志和操作日志

文章目录 一、前文二、白名单2.1 配置文件iotdb-common.properties2.2 配置文件white.list2.3 注意事项 三、审计日志3.1 Cli操作日志3.2 RESTful操作日志3.3 MQTT操作日志3.4 Java操作日志3.5 C#操作日志3.6 Python操作日志 四、参考 一、前文 IoTDB入门教程——导读 IoTDB企业…

C语言9~10 DAY(合集)

数组的概念 什么是数组 数组是相同类型&#xff0c;有序数据的集合。 数组的特征 数组中的数据被称为数组的元素&#xff0c;是同构的 数组中的元素存放在内存空间里 (char player_name[6]&#xff1a;申请在内存中开辟6块连续的基于char类型的变量空间) 衍生概念&#x…

力扣高频SQL 50题(基础版)第三十七题

文章目录 力扣高频SQL 50题&#xff08;基础版&#xff09;第三十七题176.第二高的薪水题目说明实现过程准备数据实现方式结果截图总结 力扣高频SQL 50题&#xff08;基础版&#xff09;第三十七题 176.第二高的薪水 题目说明 Employee 表&#xff1a; ----------------- …

【传知代码】基于标签相关性的多标签学习(论文复现)

在当今信息爆炸的时代&#xff0c;数据中包含的标签信息对于理解和分析复杂问题至关重要。在诸如文本分类、图像识别和推荐系统等应用中&#xff0c;如何有效地利用标签相关性提升多标签学习的效果成为了研究的热点之一。基于标签相关性的多标签学习方法&#xff0c;通过挖掘不…

存储届的奥运竞技 | 400层3D NAND最快2025到来~

随着内存巨头之间的高带宽内存 (HBM) 竞争日益激烈&#xff0c;NAND 存储器领域的竞争也在升温。据韩国媒体《etnews》报道&#xff0c;SK 海力士正在研发 400 层 NAND 闪存技术&#xff0c;计划在 2025 年底前准备好这项技术以实现量产。 报道称&#xff0c;SK 海力士目前正在…

AcWing并查集

建议先看这个 Bilibili------------------>图论——并查集(详细版) 其实M 1 2就是把1的祖先改成了2&#xff0c;然后M 3 4就是把3的祖先改成了4&#xff0c;然后查询这两数1,2的祖先是不是同一个&#xff0c;3,4的祖先是不是同一个&#xff0c;1&#xff0c;3的祖先是不是同…

【期货】收盘点评。昨天说的,p2409棕榈油在今天或者周一会走出行情

收盘点评 昨天说的&#xff0c;p2409棕榈油在今天或者周一会走出行情。事实就是如此。震荡了几天了&#xff0c;波幅不大的来回震荡&#xff0c;其实主力是不想震荡的&#xff0c;但是不震荡自己的货和行情走不出来。所以我昨天就说&#xff0c;应该就是这一两天会走出一波小行…

⑤【从0制作自己的ros导航小车:上、下位机通信篇】上、下位机串口DMA通信

从0制作自己的ros导航小车 前言一、准备工作二、下位机端&#xff08;STM32&#xff09;三、上位机端&#xff08;旭日x3派&#xff09;四、测试 系列文章&#xff1a; ①【从0制作自己的ros导航小车&#xff1a;介绍及准备】 ②【从0制作自己的ros导航小车&#xff1a;下位机篇…