chenjun1_wd

图片下载过时方法替换

@@ -91,6 +91,8 @@ export { OperRowListView } from './src/main/ets/components/view/OperRowListView' @@ -91,6 +91,8 @@ export { OperRowListView } from './src/main/ets/components/view/OperRowListView'
91 91
92 export { LiveOperRowListView } from './src/main/ets/components/view/LiveOperRowListView'; 92 export { LiveOperRowListView } from './src/main/ets/components/view/LiveOperRowListView';
93 93
  94 +export { SaveNetWorkPictures } from './src/main/ets/components/SaveNetWorkPictures';
  95 +
94 export { ImageDownloadComponent } from './src/main/ets/components/ImageDownloadComponent'; 96 export { ImageDownloadComponent } from './src/main/ets/components/ImageDownloadComponent';
95 97
96 export { PageRepository } from './src/main/ets/repository/PageRepository'; 98 export { PageRepository } from './src/main/ets/repository/PageRepository';
  1 +/*
  2 + * Copyright (c) 2024 Huawei Device Co., Ltd.
  3 + * Licensed under the Apache License, Version 2.0 (the "License");
  4 + * you may not use this file except in compliance with the License.
  5 + * You may obtain a copy of the License at
  6 + *
  7 + * http://www.apache.org/licenses/LICENSE-2.0
  8 + *
  9 + * Unless required by applicable law or agreed to in writing, software
  10 + * distributed under the License is distributed on an "AS IS" BASIS,
  11 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12 + * See the License for the specific language governing permissions and
  13 + * limitations under the License.
  14 + */
  15 +
  16 +import http from '@ohos.net.http';
  17 +import ResponseCode from '@ohos.net.http';
  18 +import { image } from '@kit.ImageKit';
  19 +import { BusinessError } from '@ohos.base';
  20 +import common from '@ohos.app.ability.common';
  21 +import photoAccessHelper from '@ohos.file.photoAccessHelper';
  22 +import fs from '@ohos.file.fs';
  23 +import promptAction from '@ohos.promptAction';
  24 +import picker from '@ohos.file.picker';
  25 +import { taskpool } from '@kit.ArkTS';
  26 +import { httpRequest } from '../utils/httpRequest';
  27 +
  28 +
  29 +/**
  30 + * 实现步骤:
  31 + * 点击上部“下载”按钮进入”下载网络图片到手机相册”场景示例;点击下部“下载到指定路径”按钮进入”下载文件到指定用户目录”场景示例,
  32 + * 从而实现手机相册并进行网络图片的下载和保存。
  33 + * photoAccessHelper参考文档:
  34 + * https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-photoaccesshelper
  35 + * saveButton参考文档
  36 + * https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/savebutton-0000001820999677
  37 + */
  38 +@Concurrent
  39 +async function getPicture(imageUrl: string): Promise<ArrayBuffer> {
  40 + let ret: ArrayBuffer = await new Promise((resolve, reject) => {
  41 + httpRequest.httpRequestInStream(imageUrl, (res: ArrayBuffer) => {
  42 + resolve(res); // 成功时解析Promise
  43 + }, () => {
  44 + // 下载失败时弹窗提示检查网络
  45 + promptAction.showToast({
  46 + message: $r('app.string.image_request_fail'),
  47 + duration: 2000
  48 + });
  49 + reject(new Error('Image download failed')); // 失败时拒绝Promise
  50 + });
  51 + });
  52 + return ret
  53 +}
  54 +
  55 +@Component
  56 +export struct SaveNetWorkPictures {
  57 + @State image: PixelMap | undefined = undefined;
  58 + @State photoAccessHelper: photoAccessHelper.PhotoAccessHelper | undefined = undefined; // 相册模块管理实例
  59 + @State imageBuffer: ArrayBuffer | undefined = undefined; // 图片ArrayBuffer
  60 + // @Prop @Watch('onChangeUrl') url: string = ''
  61 + @State url: string = ''
  62 +
  63 + /**
  64 + * 通过http的request方法从网络下载图片资源
  65 + */
  66 + async getPicture() {
  67 + http.createHttp()// 显示网络图片的地址
  68 + // .request('https://gitee.com/harmonyos-cases/cases/raw/master/CommonAppDevelopment/feature/variablewatch/src/main/resources/base/media/variablewatch_grape.png',
  69 + // .request('https://cdnjdphoto.aikan.pdnews.cn/zhbj-20240830/image/content/4d61839662044ac796ec7331a90da5d8.jpg',
  70 + .request(this.url,
  71 + (error: BusinessError, data: http.HttpResponse) => {
  72 + if (error) {
  73 + // 下载失败时弹窗提示检查网络,不执行后续逻辑
  74 + promptAction.showToast({
  75 + message: '下载失败',
  76 + duration: 2000
  77 + })
  78 + return;
  79 + }
  80 + this.transcodePixelMap(data);
  81 + // 判断网络获取到的资源是否为ArrayBuffer类型
  82 + if (data.result instanceof ArrayBuffer) {
  83 + this.imageBuffer = data.result as ArrayBuffer;
  84 + }
  85 + }
  86 + )
  87 + }
  88 +
  89 + /**
  90 + * 使用createPixelMap将ArrayBuffer类型的图片装换为PixelMap类型
  91 + * @param data:网络获取到的资源
  92 + */
  93 + transcodePixelMap(data: http.HttpResponse) {
  94 + if (ResponseCode.ResponseCode.OK === data.responseCode) {
  95 + const imageData: ArrayBuffer = data.result as ArrayBuffer;
  96 + // 通过ArrayBuffer创建图片源实例。
  97 + const imageSource: image.ImageSource = image.createImageSource(imageData);
  98 + const options: image.InitializationOptions = {
  99 + 'alphaType': 0, // 透明度
  100 + 'editable': false, // 是否可编辑
  101 + 'pixelFormat': 3, // 像素格式
  102 + 'scaleMode': 1, // 缩略值
  103 + 'size': { height: 50, width: 50 }
  104 + }; // 创建图片大小
  105 +
  106 + // 通过属性创建PixelMap
  107 + imageSource.createPixelMap(options).then((pixelMap: PixelMap) => {
  108 + this.image = pixelMap;
  109 + });
  110 + }
  111 + }
  112 +
  113 + /**
  114 + * 保存ArrayBuffer到图库
  115 + * @param buffer:图片ArrayBuffer
  116 + * @returns
  117 + */
  118 + async saveImage(buffer: ArrayBuffer | string): Promise<void> {
  119 + const context = getContext(this) as common.UIAbilityContext; // 获取getPhotoAccessHelper需要的context
  120 + const helper = photoAccessHelper.getPhotoAccessHelper(context); // 获取相册管理模块的实例
  121 + const uri = await helper.createAsset(photoAccessHelper.PhotoType.IMAGE, 'jpg'); // 指定待创建的文件类型、后缀和创建选项,创建图片或视频资源
  122 + const file = await fs.open(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
  123 + await fs.write(file.fd, buffer);
  124 + await fs.close(file.fd);
  125 + }
  126 +
  127 + /**
  128 + * 保存ArrayBuffer到用户选择的路径
  129 + * @param buffer:图片ArrayBuffer
  130 + * @returns
  131 + */
  132 + async pickerSave(buffer: ArrayBuffer | string): Promise<void> {
  133 + const photoSaveOptions = new picker.PhotoSaveOptions(); // 创建文件管理器保存选项实例
  134 + photoSaveOptions.newFileNames = ['PhotoViewPicker ' + new Date().getTime() + '.jpg']; // 保存文件名(可选)
  135 + const photoViewPicker = new picker.PhotoViewPicker;
  136 + photoViewPicker.save(photoSaveOptions)
  137 + .then(async (photoSvaeResult) => {
  138 + const uri = photoSvaeResult[0];
  139 + const file = await fs.open(uri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
  140 + await fs.write(file.fd, buffer);
  141 + await fs.close(file.fd);
  142 + promptAction.showToast({
  143 + message: '保存成功',
  144 + duration: 2000
  145 + })
  146 + });
  147 + }
  148 +
  149 + async aboutToAppear(): Promise<void> {
  150 + // this.getPicture();
  151 + this.onChangeUrl()
  152 + }
  153 +
  154 + build() {
  155 + Stack({ alignContent: Alignment.Center }) {
  156 + // Column() {
  157 + // Text('下载')
  158 + // .fontWeight(FontWeight.Medium)
  159 + // }
  160 +
  161 + Image(this.image)
  162 + .objectFit(ImageFit.Contain)
  163 + .width('50%')
  164 + .height(28)
  165 +
  166 + SaveButton({ icon: SaveIconStyle.FULL_FILLED, buttonType: ButtonType.Capsule })
  167 + .iconSize(24)
  168 + .backgroundColor(Color.Transparent)
  169 + .iconColor(Color.White)// .markAnchor({ x: '100%' })
  170 + .position({ x: '25%' })
  171 + .onClick(async () => {
  172 + if (this.imageBuffer !== undefined) {
  173 + await this.saveImage(this.imageBuffer);
  174 + promptAction.showToast({
  175 + message: '成功',
  176 + duration: 2000
  177 + })
  178 + }
  179 + })
  180 +
  181 + }
  182 +
  183 + // .alignSelf(ItemAlign.Center)
  184 + // .align(Alignment.Center)
  185 + // .backgroundColor(Color.Red)
  186 + }
  187 +
  188 + async onChangeUrl(): Promise<void> {
  189 + console.info(`cj2024 图片下载 ${this.url}`)
  190 + // const context = getContext(this) as common.UIAbilityContext;
  191 + // const atManager = abilityAccessCtrl.createAtManager();
  192 + // await atManager.requestPermissionsFromUser(context, PERMISSIONS);
  193 + // 通过任务池(taskpool)从网络下载图片资源
  194 + taskpool.execute(getPicture, this.url).then((res) => {
  195 + const imgBuffer = res as ArrayBuffer
  196 + this.imageBuffer = imgBuffer;
  197 + })
  198 + }
  199 +}
1 import { PhotoListBean } from 'wdBean/Index'; 1 import { PhotoListBean } from 'wdBean/Index';
2 import { display, router } from '@kit.ArkUI'; 2 import { display, router } from '@kit.ArkUI';
3 -import { ImageDownloadComponent } from '../components/ImageDownloadComponent';  
4 import { MultiPictureDetailItemComponent } from '../components/MultiPictureDetailItemComponent'; 3 import { MultiPictureDetailItemComponent } from '../components/MultiPictureDetailItemComponent';
5 4
6 import { Action } from 'wdBean'; 5 import { Action } from 'wdBean';
7 import { WindowModel } from 'wdKit/Index'; 6 import { WindowModel } from 'wdKit/Index';
  7 +import { SaveNetWorkPictures } from '../components/SaveNetWorkPictures';
8 8
9 const TAG = 'MultiPictureListPage'; 9 const TAG = 'MultiPictureListPage';
10 10
@@ -31,7 +31,6 @@ export struct MultiPictureListPage { @@ -31,7 +31,6 @@ export struct MultiPictureListPage {
31 PageTransitionExit({ duration: !this.noAnimation ? 0 : 300 }) 31 PageTransitionExit({ duration: !this.noAnimation ? 0 : 300 })
32 } 32 }
33 33
34 -  
35 aboutToAppear(): void { 34 aboutToAppear(): void {
36 //获取宽高尺寸 35 //获取宽高尺寸
37 this.screenWidth = this.displayTool.width 36 this.screenWidth = this.displayTool.width
@@ -47,17 +46,17 @@ export struct MultiPictureListPage { @@ -47,17 +46,17 @@ export struct MultiPictureListPage {
47 46
48 onPageShow(): void { 47 onPageShow(): void {
49 console.log(TAG, 'onPageShow') 48 console.log(TAG, 'onPageShow')
50 - WindowModel.shared.setWindowSystemBarProperties({ statusBarContentColor: '#ffffff'}) 49 + WindowModel.shared.setWindowSystemBarProperties({ statusBarContentColor: '#ffffff' })
51 } 50 }
52 51
53 onPageHide(): void { 52 onPageHide(): void {
54 console.log(TAG, 'onPageHide') 53 console.log(TAG, 'onPageHide')
55 - WindowModel.shared.setWindowSystemBarProperties({ statusBarContentColor: '#000000'}) 54 + WindowModel.shared.setWindowSystemBarProperties({ statusBarContentColor: '#000000' })
56 } 55 }
57 56
58 build() { 57 build() {
59 RelativeContainer() { 58 RelativeContainer() {
60 - Row(){ 59 + Row() {
61 Image($r('app.media.icon_arrow_left_white')) 60 Image($r('app.media.icon_arrow_left_white'))
62 .width(24) 61 .width(24)
63 .height(24) 62 .height(24)
@@ -121,7 +120,9 @@ export struct MultiPictureListPage { @@ -121,7 +120,9 @@ export struct MultiPictureListPage {
121 } 120 }
122 .alignSelf(ItemAlign.Center) 121 .alignSelf(ItemAlign.Center)
123 .fontColor(Color.White) 122 .fontColor(Color.White)
124 - ImageDownloadComponent({ url: this.currentUrl }) 123 +
  124 + // ImageDownloadComponent({ url: this.currentUrl })
  125 + SaveNetWorkPictures({ url: this.currentUrl })
125 } 126 }
126 .margin({ 127 .margin({
127 top: 14, 128 top: 14,
@@ -129,6 +130,7 @@ export struct MultiPictureListPage { @@ -129,6 +130,7 @@ export struct MultiPictureListPage {
129 bottom: 14, 130 bottom: 14,
130 right: 0 131 right: 0
131 }) 132 })
  133 + // .backgroundColor(Color.Blue)
132 .id('e_swiper_titles') 134 .id('e_swiper_titles')
133 .alignRules({ 135 .alignRules({
134 bottom: { anchor: "__container__", align: VerticalAlign.Bottom }, 136 bottom: { anchor: "__container__", align: VerticalAlign.Bottom },
1 -import { NetworkUtil, Logger, SPHelper, StringUtils } from 'wdKit'; 1 +import { Logger, NetworkUtil, SPHelper, StringUtils } from 'wdKit';
2 import { ResponseDTO } from 'wdNetwork'; 2 import { ResponseDTO } from 'wdNetwork';
3 import { 3 import {
4 ContentDetailDTO, 4 ContentDetailDTO,
  5 + InteractDataDTO,
  6 + Params,
5 PhotoListBean, 7 PhotoListBean,
6 - postInteractBrowsOperateParams,  
7 postBatchAttentionStatusParams, 8 postBatchAttentionStatusParams,
8 postInteractAccentionOperateParams, 9 postInteractAccentionOperateParams,
9 - Params,  
10 - InteractDataDTO 10 + postInteractBrowsOperateParams
11 } from 'wdBean'; 11 } from 'wdBean';
12 import { DateTimeUtils } from 'wdKit/Index'; 12 import { DateTimeUtils } from 'wdKit/Index';
13 import { WDRouterPage, WDRouterRule } from 'wdRouter/Index'; 13 import { WDRouterPage, WDRouterRule } from 'wdRouter/Index';
14 import { SpConstants } from 'wdConstant/Index'; 14 import { SpConstants } from 'wdConstant/Index';
15 import { common } from '@kit.AbilityKit'; 15 import { common } from '@kit.AbilityKit';
16 import { CommentDialogView } from 'wdDetailPlayShortVideo/Index'; 16 import { CommentDialogView } from 'wdDetailPlayShortVideo/Index';
17 -import { EmptyComponent,  
18 - ImageDownloadComponent, 17 +import {
  18 + EmptyComponent,
19 MultiPictureDetailItemComponent, 19 MultiPictureDetailItemComponent,
20 MultiPictureDetailViewModel, 20 MultiPictureDetailViewModel,
21 OperRowListView, 21 OperRowListView,
22 PageRepository, 22 PageRepository,
23 publishCommentModel, 23 publishCommentModel,
24 - viewBlogItemInsightIntentShare} from 'wdComponent/Index';  
25 -import { ParamType, TrackConstants, TrackingButton, TrackingContent } from 'wdTracking/Index'; 24 + SaveNetWorkPictures,
  25 + viewBlogItemInsightIntentShare
  26 +} from 'wdComponent/Index';
  27 +import { ParamType, TrackConstants, TrackingContent } from 'wdTracking/Index';
26 28
27 const TAG = 'MultiPictureDetailPageComponent'; 29 const TAG = 'MultiPictureDetailPageComponent';
  30 +
28 /** 31 /**
29 * 多图(图集详情页)UI 32 * 多图(图集详情页)UI
30 */ 33 */
@@ -96,7 +99,7 @@ export struct MultiPictureDetailPageComponent { @@ -96,7 +99,7 @@ export struct MultiPictureDetailPageComponent {
96 this.contentTrackingDict() 99 this.contentTrackingDict()
97 } 100 }
98 101
99 - contentTrackingDict(){ 102 + contentTrackingDict() {
100 this.pageParam = { 103 this.pageParam = {
101 'contentType': `${this.contentDetailData.newsType}`, 104 'contentType': `${this.contentDetailData.newsType}`,
102 'contentId': `${this.contentDetailData.newsId}`, 105 'contentId': `${this.contentDetailData.newsId}`,
@@ -138,6 +141,7 @@ export struct MultiPictureDetailPageComponent { @@ -138,6 +141,7 @@ export struct MultiPictureDetailPageComponent {
138 bottom: { anchor: "__container__", align: VerticalAlign.Bottom }, 141 bottom: { anchor: "__container__", align: VerticalAlign.Bottom },
139 middle: { anchor: "__container__", align: HorizontalAlign.Center } 142 middle: { anchor: "__container__", align: HorizontalAlign.Center }
140 }) 143 })
  144 +
141 CommentDialogView({ 145 CommentDialogView({
142 index: $index, 146 index: $index,
143 currentIndex: $currentIndex, 147 currentIndex: $currentIndex,
@@ -156,7 +160,7 @@ export struct MultiPictureDetailPageComponent { @@ -156,7 +160,7 @@ export struct MultiPictureDetailPageComponent {
156 @Builder 160 @Builder
157 rmh() { 161 rmh() {
158 if (!this.showDownload) { 162 if (!this.showDownload) {
159 - Row(){ 163 + Row() {
160 Row() { 164 Row() {
161 Row({ space: 8 }) { 165 Row({ space: 8 }) {
162 if (this.getImgUrl()) { 166 if (this.getImgUrl()) {
@@ -257,7 +261,7 @@ export struct MultiPictureDetailPageComponent { @@ -257,7 +261,7 @@ export struct MultiPictureDetailPageComponent {
257 Image($r('app.media.add')) 261 Image($r('app.media.add'))
258 .width(12) 262 .width(12)
259 .height(12) 263 .height(12)
260 - .margin({right: 3}) 264 + .margin({ right: 3 })
261 265
262 Text('关注').fontSize(12).fontColor(0xffffff) 266 Text('关注').fontSize(12).fontColor(0xffffff)
263 }.alignItems(VerticalAlign.Center) 267 }.alignItems(VerticalAlign.Center)
@@ -268,7 +272,8 @@ export struct MultiPictureDetailPageComponent { @@ -268,7 +272,8 @@ export struct MultiPictureDetailPageComponent {
268 .height(24) 272 .height(24)
269 .onClick(() => { 273 .onClick(() => {
270 this.handleAccention() 274 this.handleAccention()
271 - }).visibility(this.isShowButton ? Visibility.Visible : Visibility.None) 275 + })
  276 + .visibility(this.isShowButton ? Visibility.Visible : Visibility.None)
272 } else { 277 } else {
273 Button({ type: ButtonType.Normal, stateEffect: true }) { 278 Button({ type: ButtonType.Normal, stateEffect: true }) {
274 Row() { 279 Row() {
@@ -281,7 +286,8 @@ export struct MultiPictureDetailPageComponent { @@ -281,7 +286,8 @@ export struct MultiPictureDetailPageComponent {
281 .height(24) 286 .height(24)
282 .onClick(() => { 287 .onClick(() => {
283 this.handleAccention() 288 this.handleAccention()
284 - }).visibility(this.isShowButton ? Visibility.Visible : Visibility.None) 289 + })
  290 + .visibility(this.isShowButton ? Visibility.Visible : Visibility.None)
285 } 291 }
286 292
287 } 293 }
@@ -429,6 +435,7 @@ export struct MultiPictureDetailPageComponent { @@ -429,6 +435,7 @@ export struct MultiPictureDetailPageComponent {
429 ListItem() { 435 ListItem() {
430 this.ListItemTitle() 436 this.ListItemTitle()
431 } 437 }
  438 +
432 ListItem() { 439 ListItem() {
433 this.ListItemDescription() 440 this.ListItemDescription()
434 } 441 }
@@ -499,11 +506,18 @@ export struct MultiPictureDetailPageComponent { @@ -499,11 +506,18 @@ export struct MultiPictureDetailPageComponent {
499 } 506 }
500 507
501 if (this.contentDetailData.photoList?.[this.swiperIndex].picPath) { 508 if (this.contentDetailData.photoList?.[this.swiperIndex].picPath) {
502 - ImageDownloadComponent({ url: this.contentDetailData.photoList?.[this.swiperIndex].picPath }) 509 + // ImageDownloadComponent({ url: this.contentDetailData.photoList?.[this.swiperIndex].picPath })
  510 + // .parallelGesture(
  511 + // TapGesture()
  512 + // .onAction((event: GestureEvent) => {
  513 + // TrackingContent.download(1,TrackConstants.PageName.Atlas_Detail,TrackConstants.PageName.Atlas_Detail,this.pageParam)
  514 + // }))
  515 + SaveNetWorkPictures({ url: this.contentDetailData.photoList?.[this.swiperIndex].picPath })
503 .parallelGesture( 516 .parallelGesture(
504 TapGesture() 517 TapGesture()
505 .onAction((event: GestureEvent) => { 518 .onAction((event: GestureEvent) => {
506 - TrackingContent.download(1,TrackConstants.PageName.Atlas_Detail,TrackConstants.PageName.Atlas_Detail,this.pageParam) 519 + TrackingContent.download(1, TrackConstants.PageName.Atlas_Detail, TrackConstants.PageName.Atlas_Detail,
  520 + this.pageParam)
507 })) 521 }))
508 } 522 }
509 } 523 }
@@ -521,7 +535,10 @@ export struct MultiPictureDetailPageComponent { @@ -521,7 +535,10 @@ export struct MultiPictureDetailPageComponent {
521 @Builder 535 @Builder
522 noNet() { 536 noNet() {
523 EmptyComponent({ 537 EmptyComponent({
524 - emptyType: 1, emptyButton: true, isBlack: true, retry: () => { 538 + emptyType: 1,
  539 + emptyButton: true,
  540 + isBlack: true,
  541 + retry: () => {
525 this.getDetail() 542 this.getDetail()
526 } 543 }
527 }) 544 })
@@ -659,10 +676,12 @@ export struct MultiPictureDetailPageComponent { @@ -659,10 +676,12 @@ export struct MultiPictureDetailPageComponent {
659 // console.log(TAG, '关注号主==', JSON.stringify(res.data)) 676 // console.log(TAG, '关注号主==', JSON.stringify(res.data))
660 if (this.followStatus == '1') { 677 if (this.followStatus == '1') {
661 this.followStatus = '0' 678 this.followStatus = '0'
662 - TrackingContent.follow(true,this.followUserId,this.followUserName,TrackConstants.PageName.Atlas_Detail,TrackConstants.PageName.Atlas_Detail,this.pageParam) 679 + TrackingContent.follow(true, this.followUserId, this.followUserName, TrackConstants.PageName.Atlas_Detail,
  680 + TrackConstants.PageName.Atlas_Detail, this.pageParam)
663 } else { 681 } else {
664 this.followStatus = '1' 682 this.followStatus = '1'
665 - TrackingContent.follow(false,this.followUserId,this.followUserName,TrackConstants.PageName.Atlas_Detail,TrackConstants.PageName.Atlas_Detail,this.pageParam) 683 + TrackingContent.follow(false, this.followUserId, this.followUserName, TrackConstants.PageName.Atlas_Detail,
  684 + TrackConstants.PageName.Atlas_Detail, this.pageParam)
666 } 685 }
667 }) 686 })
668 } 687 }