UE4 从网络端下载图片并且缓存到本地

从网络端下载一个图片,并且在UI上显示出来,然后再缓存到本地文件中,方便下次使用。

下载图片

UE4官方提供了一个异步任务类,专门处理下载图片的需求。

DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDownloadImageDelegate, UTexture2DDynamic*, Texture);UCLASS()
class UMG_API UAsyncTaskDownloadImage : public UBlueprintAsyncActionBase
{GENERATED_UCLASS_BODY()public:UFUNCTION(BlueprintCallable, meta=( BlueprintInternalUseOnly="true" ))static UAsyncTaskDownloadImage* DownloadImage(FString URL);public:UPROPERTY(BlueprintAssignable)FDownloadImageDelegate OnSuccess;UPROPERTY(BlueprintAssignable)FDownloadImageDelegate OnFail;public:void Start(FString URL);private:/** Handles image requests coming from the web */void HandleImageRequest(FHttpRequestPtr HttpRequest, FHttpResponsePtr HttpResponse, bool bSucceeded);
};
void UAsyncTaskDownloadImage::HandleImageRequest(FHttpRequestPtr HttpRequest, FHttpResponsePtr HttpResponse, bool bSucceeded)
{#if !UE_SERVERRemoveFromRoot();if ( bSucceeded && HttpResponse.IsValid() && HttpResponse->GetContentLength() > 0 ){IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));TSharedPtr<IImageWrapper> ImageWrappers[3] ={ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG),ImageWrapperModule.CreateImageWrapper(EImageFormat::JPEG),ImageWrapperModule.CreateImageWrapper(EImageFormat::BMP),};for ( auto ImageWrapper : ImageWrappers ){if ( ImageWrapper.IsValid() && ImageWrapper->SetCompressed(HttpResponse->GetContent().GetData(), HttpResponse->GetContentLength()) ){TArray64<uint8>* RawData = new TArray64<uint8>();const ERGBFormat InFormat = ERGBFormat::BGRA;if ( ImageWrapper->GetRaw(InFormat, 8, *RawData) ){if ( UTexture2DDynamic* Texture = UTexture2DDynamic::Create(ImageWrapper->GetWidth(), ImageWrapper->GetHeight()) ){Texture->SRGB = true;Texture->UpdateResource();FTexture2DDynamicResource* TextureResource = static_cast<FTexture2DDynamicResource*>(Texture->Resource);if (TextureResource){ENQUEUE_RENDER_COMMAND(FWriteRawDataToTexture)([TextureResource, RawData](FRHICommandListImmediate& RHICmdList){WriteRawToTexture_RenderThread(TextureResource, RawData);});}else{delete RawData;}OnSuccess.Broadcast(Texture);                        return;}}}}}OnFail.Broadcast(nullptr);#endif
}static void WriteRawToTexture_RenderThread(FTexture2DDynamicResource* TextureResource, TArray64<uint8>* RawData, bool bUseSRGB = true)
{check(IsInRenderingThread());if (TextureResource){FRHITexture2D* TextureRHI = TextureResource->GetTexture2DRHI();int32 Width = TextureRHI->GetSizeX();int32 Height = TextureRHI->GetSizeY();uint32 DestStride = 0;uint8* DestData = reinterpret_cast<uint8*>(RHILockTexture2D(TextureRHI, 0, RLM_WriteOnly, DestStride, false, false));for (int32 y = 0; y < Height; y++){uint8* DestPtr = &DestData[((int64)Height - 1 - y) * DestStride];const FColor* SrcPtr = &((FColor*)(RawData->GetData()))[((int64)Height - 1 - y) * Width];for (int32 x = 0; x < Width; x++){*DestPtr++ = SrcPtr->B;*DestPtr++ = SrcPtr->G;*DestPtr++ = SrcPtr->R;*DestPtr++ = SrcPtr->A;SrcPtr++;}}RHIUnlockTexture2D(TextureRHI, 0, false, false);}delete RawData;
}

根据以上内容可以看出,利用Http协议从网络端获取图片的数据后,应对PNG,JPEG,BMP三种图片格式来进行读取,如果读取成功,则把数据写入到RenderThread线程中,渲染为UTexture2DDynamic内部的Resource,实现UTexture2DDynamic对象的生成,用于直接从OnSuccess函数中返回,这里初步实现了下载功能。

保存图片

到保存图片这步时候,我先实现了一个方案,基于UTexture2D的本地存储。

bool ImageManager::SaveTextureToTempDir(const FString& ImageName, UTexture2D* Texture)
{if (!Texture || ImageName.IsEmpty())return false;//FTexture2DMipMap& MipMap = Texture->PlatformData->Mips[0];FTexturePlatformData** pPlatformData = Texture->GetRunningPlatformData();if (!pPlatformData){return false;}FTexture2DMipMap& MipMap = (*pPlatformData)->Mips[0];unsigned char* Data = (unsigned char*)MipMap.BulkData.Lock(LOCK_READ_WRITE);int32 TexSizeX = MipMap.SizeX;int32 TexSizeY = MipMap.SizeY;TArray<FColor> nColors;nColors.SetNum(TexSizeX * TexSizeY);FMemory::Memcpy(nColors.GetData(), Data, (int32)(sizeof(FColor)) * TexSizeX * TexSizeY);MipMap.BulkData.Unlock();TArray<uint8> ImgData;FImageUtils::CompressImageArray(TexSizeX, TexSizeY, nColors, ImgData);FString FilePath = FPaths::ProjectSavedDir() + gGameConfig.GetImageSaveDir();if (!DirectoryExists(*FilePath)){MakeDirectory(*FilePath, true);}FString SaveFilePathName = FilePath / ImageName;return FFileHelper::SaveArrayToFile(ImgData, *SaveFilePathName);
}

在我想去保存UTexture2DDynamic类型的图片时候,就发生了很多问题,首先其内部信息存储结构完全不同,在努力了一阵之后发现依然无法实现,这时候我重新去找了官方的方案,查到了另外一个专门用于导出Texture的官方库类UImageWriteBlueprintLibrary。

/*** Function library containing utility methods for writing images on a global async queue*/
UCLASS()
class UImageWriteBlueprintLibrary : public UBlueprintFunctionLibrary
{public:GENERATED_BODY()static bool IMAGEWRITEQUEUE_API ResolvePixelData(UTexture* Texture, const FOnPixelsReady& OnPixelsReady);/*** Export the specified texture to disk** @param Texture         The texture or render target to export* @param Filename        The filename on disk to save as* @param Options         Parameters defining the various export options*/UFUNCTION(BlueprintCallable, Category=Texture, meta=(ScriptMethod))static IMAGEWRITEQUEUE_API void ExportToDisk(UTexture* Texture, const FString& Filename, const FImageWriteOptions& Options);
};

但是该类型,依然是只支持UTextureRenderTarget2D和UTexture2D两种类型的导出,所以经过调试研究,我对ResolvePixelData函数进行了一些改造,使它支持了UTexture2DDynamic类型的导出。

bool UImageWriteBlueprintLibrary::ResolvePixelData(UTexture* InTexture, const FOnPixelsReady& OnPixelsReady)
{if (!InTexture){FFrame::KismetExecutionMessage(TEXT("Invalid texture supplied."), ELogVerbosity::Error);return false;}EPixelFormat Format = PF_Unknown;if (UTextureRenderTarget2D* RT2D = Cast<UTextureRenderTarget2D>(InTexture)){Format = RT2D->GetFormat();}else if (UTexture2D* Texture2D = Cast<UTexture2D>(InTexture)){Format = Texture2D->GetPixelFormat();}//新增内容 startelse if(UTexture2DDynamic* TextureDynamic2D = Cast<UTexture2DDynamic>(InTexture)){Format = InTexture->Resource->TextureRHI->GetTexture2D();}//新增内容 endswitch (Format){default:FFrame::KismetExecutionMessage(TEXT("Unsupported texture format."), ELogVerbosity::Error);return false;case PF_FloatRGBA:case PF_A32B32G32R32F:case PF_R8G8B8A8:case PF_B8G8R8A8:break;}...//省略部分内容
}

至此其实已经实现了我需要的全部功能,从网络端下载图片,显示到UI,然后再存储到本地。然而其中存在一个关键性的问题,我们对于引擎的修改尽量能够导出到项目中,这样方便与他人同步,也方便在更新引擎时候不需要重复修改这部分内容,所以我对上边的方案进行了重新设计。

首先因为需要UTexture2D的类型来在内存中做一个缓存,方便不同位置使用该图片时候不用重复下载或者加载,所以我们还是需要将内存中的图片转为UTexture2D类型。所以我对最初下载图片的类进行了改造,并且放在了项目下。

void UMyAsyncTaskDownloadImage::HandleImageRequest(FHttpRequestPtr HttpRequest, FHttpResponsePtr HttpResponse, bool bSucceeded)
{#if !UE_SERVERRemoveFromRoot();if ( bSucceeded && HttpResponse.IsValid() && HttpResponse->GetContentLength() > 0 ){IImageWrapperModule& ImageWrapperModule = FModuleManager::LoadModuleChecked<IImageWrapperModule>(FName("ImageWrapper"));TSharedPtr<IImageWrapper> ImageWrappers[3] ={ImageWrapperModule.CreateImageWrapper(EImageFormat::PNG),ImageWrapperModule.CreateImageWrapper(EImageFormat::JPEG),ImageWrapperModule.CreateImageWrapper(EImageFormat::BMP),};for ( auto ImageWrapper : ImageWrappers ){if ( ImageWrapper.IsValid() && ImageWrapper->SetCompressed(HttpResponse->GetContent().GetData(), HttpResponse->GetContentLength()) ){TArray64<uint8>* RawData = new TArray64<uint8>();const ERGBFormat InFormat = ERGBFormat::BGRA;if ( ImageWrapper->GetRaw(InFormat, 8, *RawData) ){UTexture2D* Texture = gImageManager.CreateTexture2D(*RawData, ImageWrapper->GetWidth(), ImageWrapper->GetHeight());if (Texture){OnSuccess.Broadcast(Texture);                  }delete RawData;return;}}}}OnFail.Broadcast(nullptr);#endif
}//将TArray64<uint8> PixelData 的图片信息转化为UTexture2D的对象
UTexture2D * ImageManager::CreateTexture2D(const TArray64<uint8>& PixelData, int32 InSizeX, int32 InSizeY, EPixelFormat InFormat, FName BaseName)
{UTexture2D* NewTexture = nullptr;if (InSizeX > 0 && InSizeY > 0 &&(InSizeX % GPixelFormats[InFormat].BlockSizeX) == 0 &&(InSizeY % GPixelFormats[InFormat].BlockSizeY) == 0){NewTexture = NewObject<UTexture2D>(GetTransientPackage(),BaseName,RF_Transient);NewTexture->PlatformData = new FTexturePlatformData();NewTexture->PlatformData->SizeX = InSizeX;NewTexture->PlatformData->SizeY = InSizeY;NewTexture->PlatformData->PixelFormat = InFormat;NewTexture->SRGB = true;//NewTexture->CompressionSettings = TC_HDR;// Allocate first mipmap.int32 NumBlocksX = InSizeX / GPixelFormats[InFormat].BlockSizeX;int32 NumBlocksY = InSizeY / GPixelFormats[InFormat].BlockSizeY;FTexture2DMipMap* Mip = new FTexture2DMipMap();NewTexture->PlatformData->Mips.Add(Mip);Mip->SizeX = InSizeX;Mip->SizeY = InSizeY;Mip->BulkData.Lock(LOCK_READ_WRITE);void* TextureData = Mip->BulkData.Realloc(NumBlocksX * NumBlocksY * GPixelFormats[InFormat].BlockBytes);FMemory::Memcpy(TextureData, PixelData.GetData(), PixelData.Num());Mip->BulkData.Unlock();NewTexture->UpdateResource();}else{UE_LOG(LogImageManager, Warning, TEXT("Invalid parameters specified for ImageManager::CreateTexture2D()"));}return NewTexture;
}

然后在保存的时候就可以很方便了调用我最初写的 ImageManager::SaveTextureToTempDir(UTexture* InTexture, const FOnPixelsReady& OnPixelsReady) 函数,这样就算是完整的实现了所有的需求。

另外官方的

UImageWriteBlueprintLibrary::ExportToDisk(UTexture* Texture, const FString& Filename, const FImageWriteOptions& Options)

函数在我们需要保存不同类型和不同质量图片时候也可以很好的用到,其中参数如下:

USTRUCT(BlueprintType)
struct FImageWriteOptions
{GENERATED_BODY()FImageWriteOptions(): Format(EDesiredImageFormat::EXR), bOverwriteFile(true), bAsync(true){CompressionQuality = 0;}/** The desired output image format to write to disk */UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Image")EDesiredImageFormat Format;/** A callback to invoke when the image has been written, or there was an error */UPROPERTY(BlueprintReadWrite, Category="Image")FOnImageWriteComplete OnComplete;/** An image format specific compression setting. Either 0 (Default) or 1 (Uncompressed) for EXRs, or a value between 0 and 100. */UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Image")int32 CompressionQuality;/** Whether to overwrite the image if it already exists */UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Image")bool bOverwriteFile;/** Whether to perform the writing asynchronously, or to block the game thread until it is complete */UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="Image")bool bAsync;/** A native completion callback that will be called in addition to the dynamic one above. */TFunction<void(bool)> NativeOnComplete;
};

可以很直观的看到在CompressionQuality参数上,明确指出了可以保存从1%-100%质量图片,方便我们存储一些临时的图片作为缓存机制。

UE4下载与存储图片相关推荐

  1. MySQL 直接存储图片并在 html 页面中展示,点击下载

    数据库实体类: package com.easy.kotlin.picturecrawler.entityimport java.util.* import javax.persistence.*@E ...

  2. html显示mysql图片路径_MySQL MySQL 直接存储图片并在 html 页面中展示,点击下载 _好机友...

    数据库实体类:package com.easy.kotlin.picturecrawler.entity import java.util.* import javax.persistence.* @ ...

  3. redis java 存储图片_Redis 存储图片 [base64/url/path]vs[object]

    一.base64图片编解码 基本流程:从网络获取下载一张图片.然后base64编码,再base64解码,存到本地E盘根文件夹下. import java.awt.image.BufferedImage ...

  4. Python多线程下载网络URL图片的方法

    Python多线程下载网络URL图片的方法 采用多线程的方法,通过URL地址,下载资源图片 GitHub地址:https://github.com/PanJinquan/python-learning ...

  5. 分享制作精良的知识管理系统 配置SQL Server文档数据库 完美实现博客文章的的下载,存储和浏览...

    前一篇文章<分享制作精良的知识管理系统 博客备份程序 Site Rebuild>已经提到如何使用Site Rebuild来下载您所喜欢的博客文章,但是还不能实现把下载的文件导入进数据库中, ...

  6. java 存储png文件_vue图片上传及java存储图片(亲测可用)

    1.前言 在使用elementui的upload组件时,我一直无法做到上传的图片和其他数据一起提交.单纯的上传文件,java的存储图片的方式也有局限性. 我知道的后端保存图片有两种方式:一种是直接存储 ...

  7. 七牛云存储,图片储存

    七牛云存储,图片储存 概述 七牛云对象存储服务提供高可靠.强安全.低成本.可扩展的非结构化数据的存储服务.它提供简单的 Web  服务接口,可以通过七牛开发者平台或客户端存储和检索任意数量的数据,支持 ...

  8. python 存储图片_使用python存储网页上的图片实例

    使用python存储网页上的图片实例 本文介绍在已知网络图片的地址下,存储图片到本地 本文例子随便选择LOFTER上一张图片,复制图片的地址,如下图所示 在Python中输入代码 import req ...

  9. Python爬虫项目:爬取JSON数据存储Excel表格与存储图片

    随着网络的迅速发展,万维网成为大量信息的载体,如何有效地提取并利用这些信息成为一个巨大的挑战.搜索引擎(Search Engine),例如传统的通用搜索引擎AltaVista,Yahoo!和Googl ...

  10. python爬虫下载王者荣耀图片

    python爬虫下载王者荣耀图片 腾讯课堂白嫖的一堂课,大佬勿喷. import requests import jsondata = requests.get('http://pvp.qq.com/ ...

最新文章

  1. MongoDB修改器的使用1
  2. this Activity.this Activity.class
  3. 编码实现字符串转整型的函数(实现函数atoi的功能)
  4. strcpy和memcpy的区别 | strcpy和strncpy的区别
  5. php 接口测压,PHP API接口测试小工具
  6. window连接不上linux ftp_xftp怎么连linux,教你xftp怎么连linux
  7. java 调用python脚本过程_通过Java调用Python脚本
  8. 矩阵乘法递推的优化艺术
  9. uoj#348/洛谷P4221 [WC2018]州区划分(FWT)
  10. Linux 管理登陆的用户/查看/剔除
  11. .net测试学习--理解.net测试选项
  12. Android JNI(一)——NDK与JNI基础
  13. 购买服务器机柜需要考量的几个问题
  14. 终于解决“百年一遇”奇怪问题
  15. 分类问题的评价及matrix , precision, recall
  16. 正则表达式限制文本框内容
  17. JS中,把一个数字转换为字串
  18. 20201022-成信大-C语言程序设计-20201学期《C语言程序设计B》C-trainingExercises19
  19. dell 恢复介质_使用Dell OS Recovery Tool制作Windows恢复U盘
  20. Java面试官在面试时喜欢问哪些问题?

热门文章

  1. 电报telegramPC电脑端调为中文
  2. ArcGIS10.3 Desktop Server 安装教程 附下载地址
  3. echo -e <<EOF $()三种操作的意义
  4. 如何在虚拟机安装windows server 2003
  5. Ubuntu安装tftp服务器
  6. Matter 协议,IoT 智能家居混乱时代的终结者
  7. 鸿蒙谁法力最强,上古神话中,鸿蒙初开时有九大古神,鸿钧勉强上榜,烛龙位列前三...
  8. WIN10使用 NetSpeedMonitor
  9. 卡巴斯基授权文件获取网站
  10. SAS9.4安装简易教程(保姆级)附带报错处理