鸿蒙学习实战之路-Share Kit系列(5/17)-分享视频内容实战

最近好多朋友问我:“西兰花啊,我想让用户分享视频,比如短视频、录像之类的,但不知道代码怎么写?” 害,这问题可问对人了!

今天这篇,我就手把手带你实现分享视频功能,从零到一,全程不超过 5 分钟(不含调试时间)~


分享视频是啥?

分享视频就是把视频文件分享到其他应用或设备。比如:

  • 分享短视频到微信
  • 分享录像到微博
  • 分享视频到华为图库

目标设备会把视频保存到图库,方便用户查看和管理。


核心步骤

实现分享视频,就三步:

  1. 构造分享数据:创建 SharedData 对象,utd 类型设置为 general.video
  2. 构建分享控制器:创建 ShareController 对象
  3. 显示分享面板:调用 show 方法显示分享面板

完整代码示例

import { common } from '@kit.AbilityKit';
import { systemShare } from '@kit.ShareKit';

// 步骤1:构造分享数据
let shareData: systemShare.SharedData = new systemShare.SharedData({
  utd: 'general.video',
  uri: 'file://.../xxx.mp4',
  title: '分享视频',
  preview: 'file://.../preview.jpg',
  description: '这是一段分享视频'
});

// 步骤2:构建分享控制器
let controller: systemShare.ShareController = new systemShare.ShareController(shareData);

// 步骤3:显示分享面板
let uiContext: UIContext = this.getUIContext();
let context: common.UIAbilityContext = uiContext.getHostContext() as common.UIAbilityContext;

controller.show(context, {
  previewMode: systemShare.SharePreviewMode.DEFAULT,
  selectionMode: systemShare.SelectionMode.SINGLE
});

参数详解

utd - 视频类型标识

utd: 'general.video' 表示这是一个视频类型的数据。

🥦 西兰花小贴士
general.video 是通用的视频类型,适用于大部分视频分享场景。如果你需要更精确的视频类型,可以使用具体的视频格式,比如:

  • general.mp4:MP4 格式视频
  • general.avi:AVI 格式视频
  • general.mov:MOV 格式视频

uri - 视频文件路径

uri: 'file://.../xxx.mp4' 是视频文件的 URI 路径。

这个路径需要指向一个实际存在的视频文件,否则分享会失败。

🥦 西兰花警告
我有个朋友第一次分享视频,随便写了个路径,结果分享面板弹出来但分享失败,debug 了两小时才发现文件不存在!血泪教训啊朋友们!

title - 分享标题

title: '分享视频' 是分享内容的标题,会在分享面板中显示。

这个标题要简洁明了,让用户一眼就知道分享的是什么内容。

preview - 封面图

preview: 'file://.../preview.jpg' 是封面图的 URI 路径。

封面图会在分享面板中显示,提升用户体验。如果不需要封面图,可以不设置这个参数。

🥦 西兰花小贴士
封面图可以是视频的截图,也可以是自定义的图片。建议使用视频的截图,这样用户能更直观地了解视频内容。

description - 分享描述

description: '这是一段分享视频' 是分享内容的描述信息,会在分享面板中显示。

描述信息可以更详细地说明分享的内容,帮助用户理解。


在实际项目中怎么用?

上面的代码是基础实现,但在实际项目中,你可能会这样用:

示例:分享短视频

import { common } from '@kit.AbilityKit';
import { systemShare } from '@kit.ShareKit';

@Entry
@Component
struct VideoPage {
  // 视频数据
  videoTitle: string = '西兰花烹饪教程';
  videoPath: string = 'file://.../broccoli_cooking.mp4';
  videoPreview: string = 'file://.../preview.jpg';
  videoDescription: string = '教你如何烹饪美味的西兰花';

  // 分享视频
  onShareVideo() {
    // 构造分享数据
    let shareData: systemShare.SharedData = new systemShare.SharedData({
      utd: 'general.video',
      uri: this.videoPath,
      title: this.videoTitle,
      preview: this.videoPreview,
      description: this.videoDescription
    });

    // 构建分享控制器
    let controller: systemShare.ShareController = new systemShare.ShareController(shareData);

    // 显示分享面板
    let uiContext: UIContext = this.getUIContext();
    let context: common.UIAbilityContext = uiContext.getHostContext() as common.UIAbilityContext;

    controller.show(context, {
      previewMode: systemShare.SharePreviewMode.DEFAULT,
      selectionMode: systemShare.SelectionMode.SINGLE
    });
  }

  build() {
    Column() {
      Image(this.videoPreview)
        .width(200)
        .height(200)
        .margin(20);

      Text(this.videoTitle)
        .fontSize(24)
        .fontWeight(FontWeight.Bold)
        .margin({ left: 20, right: 20, bottom: 10 })
        .fontColor(Color.Black);

      Text(this.videoDescription)
        .fontSize(16)
        .margin({ left: 20, right: 20, bottom: 20 })
        .fontColor(Color.Gray);

      Button('分享视频')
        .onClick(() => {
          this.onShareVideo();
        })
        .margin(20);
    }
    .width('100%')
    .height('100%');
  }
}

如何生成视频封面图?

上面的示例中,封面图是固定的。但在实际项目中,你可能需要从视频中提取封面图。

示例:从视频中提取封面图

import { image } from '@kit.ImageKit';
import { media } from '@kit.MediaKit';
import { fileIo } from '@kit.CoreFileKit';

// 从视频中提取封面图
async function extractVideoThumbnail(videoPath: string, outputPath: string): Promise<void> {
  try {
    // 创建视频播放器
    let videoPlayer = await media.createVideoPlayer();

    // 设置视频源
    videoPlayer.src = videoPath;

    // 等待视频准备就绪
    await new Promise<void>((resolve) => {
      videoPlayer.on('playbackComplete', () => {
        resolve();
      });
    });

    // 获取视频时长
    let duration = videoPlayer.duration;

    // 跳转到视频的中间位置
    let seekTime = duration / 2;
    await videoPlayer.seek(seekTime, media.SeekMode.SEEK_NEXT_SYNC);

    // 获取当前帧
    let surfaceId = videoPlayer.getSurfaceId();
    let imageInfo = await image.createImageInfo(surfaceId);
    let pixelMap = await image.createPixelMap(imageInfo);

    // 保存为图片
    let file = await fileIo.open(outputPath, fileIo.OpenMode.CREATE | fileIo.OpenMode.WRITE_ONLY);
    let imagePacker = image.createImagePacker();
    let packOpts = image.PackingOption();
    let packedData = await imagePacker.packing(pixelMap, packOpts);
    await fileIo.write(file.fd, packedData);
    await fileIo.close(file.fd);

    // 释放资源
    pixelMap.release();
    videoPlayer.release();
  } catch (e) {
    console.error(`提取视频封面图失败:${e}`);
  }
}

// 使用示例
extractVideoThumbnail('file://.../xxx.mp4', 'file://.../preview.jpg');

🥦 西兰花小贴士
上面的代码是一个简化的示例,实际使用时可能需要根据你的需求进行调整。比如,你可以选择视频的任意一帧作为封面图,而不仅仅是中间帧。


如何从相册选择视频并分享?

上面的示例中,视频路径是固定的。但在实际项目中,你可能需要让用户从相册选择视频,然后分享。

示例:从相册选择视频并分享

import { common } from '@kit.AbilityKit';
import { systemShare } from '@kit.ShareKit';
import { picker } from '@kit.CoreFileKit';

@Entry
@Component
struct GalleryVideoSharePage {
  // 选中的视频路径
  selectedVideo: string = '';

  // 从相册选择视频
  async selectVideoFromGallery(): Promise<void> {
    try {
      // 创建视频选择器
      let videoPicker = new picker.PhotoViewPicker();

      // 选择视频
      let result = await videoPicker.select({
        MIMEType: picker.PhotoViewMIMETypes.VIDEO_TYPE,
        maxSelectNumber: 1
      });

      // 获取选中的视频路径
      if (result.photoUris && result.photoUris.length > 0) {
        this.selectedVideo = result.photoUris[0];
      }
    } catch (e) {
      console.error(`选择视频失败:${e}`);
    }
  }

  // 分享选中的视频
  onShareSelectedVideo() {
    if (!this.selectedVideo) {
      console.error('请先选择视频');
      return;
    }

    // 构造分享数据
    let shareData: systemShare.SharedData = new systemShare.SharedData({
      utd: 'general.video',
      uri: this.selectedVideo,
      title: '分享视频',
      preview: this.selectedVideo
    });

    // 构建分享控制器
    let controller: systemShare.ShareController = new systemShare.ShareController(shareData);

    // 显示分享面板
    let uiContext: UIContext = this.getUIContext();
    let context: common.UIAbilityContext = uiContext.getHostContext() as common.UIAbilityContext;

    controller.show(context, {
      previewMode: systemShare.SharePreviewMode.DEFAULT,
      selectionMode: systemShare.SelectionMode.SINGLE
    });
  }

  build() {
    Column() {
      Text('从相册选择视频并分享')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
        .margin(20)
        .fontColor(Color.Black);

      if (this.selectedVideo) {
        Text(this.selectedVideo)
          .fontSize(16)
          .margin(20)
          .fontColor(Color.Gray);
      }

      Row() {
        Button('选择视频')
          .onClick(() => {
            this.selectVideoFromGallery();
          })
          .margin(10);

        Button('分享视频')
          .onClick(() => {
            this.onShareSelectedVideo();
          })
          .margin(10);
      }
      .margin(20);
    }
    .width('100%')
    .height('100%');
  }
}

常见问题

Q1:分享视频时提示"文件不存在"怎么办?

检查以下几点:

  1. 文件路径是否正确

    • 确认 uri 指向的文件是否存在
    • 确认 uri 格式是否正确(file:// 开头)
  2. 文件权限是否正确

    • 确认应用是否有读取该文件的权限

Q2:如何分享不同格式的视频?

不同格式的视频,分享方式是一样的,只需要确保 uri 指向正确格式的视频文件即可:

  • MP4 格式file://.../xxx.mp4
  • AVI 格式file://.../xxx.avi
  • MOV 格式file://.../xxx.mov

Q3:如何限制分享视频的大小?

Share Kit 对分享数据量有限制,单次分享的分享数据描述信息总量不能超过 200KB。

如果视频过大,可以考虑:

  1. 压缩视频:在分享前先压缩视频
  2. 降低分辨率:降低视频的分辨率和码率
  3. 改为分享链接:如果视频是网络视频,可以改为分享视频链接

下一步学什么?

看完这篇,你应该已经能实现分享视频功能了。接下来可以深入学习:

  1. 分享链接内容:分享 App Linking 或普通链接
  2. 高级功能:自定义分享面板、获取分享结果
  3. 目标应用接入:让你的应用能接收其他应用分享的视频
  4. 跨端分享:碰一碰分享、隔空传送

推荐资料

📚 官方文档


我是盐焗西兰花,
不教理论,只给你能跑的代码和避坑指南。
下期见!🥦

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐