다음 네 가지 타입의 DownloadHandler
가 있습니다.
DownloadHandlerBuffer
는 단순 데이터 스토리지로 사용됩니다.DownloadHandlerTexture
는 이미지를 다운로드하는 데 사용됩니다.DownloadHandlerAssetBundle
은 에셋 번들을 가져오는 데 사용됩니다.DownloadHandlerScript
는 특별한 클래스입니다. 자체적으로는 아무 역할도 하지 않지만, 이 클래스는 사용자가 정의한 클래스에 의해 상속될 수 있습니다. 이 클래스는 UnityWebRequest 시스템에서 콜백을 수신하며, 이 콜백은 네트워크에서 도착한 데이터를 완전히 커스터마이즈하여 처리하는 데 사용할 수 있습니다.오디오 클립용으로 특화된 다운로드 핸들러도 사용할 수 있습니다. API는 DownloadHandlerTexture
의 인터페이스와 유사합니다.
이 다운로드 핸들러는 가장 단순한 형태이며, 대부분의 사용 사례를 처리합니다. 이 다운로드 핸들러는 수신한 데이터를 네이티브 코드 버퍼에 저장합니다. 다운로드가 완료되면 바이트 배열 또는 UTF8 문자열로 버퍼링된 데이터에 액세스할 수 있습니다.
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
class MyBehaviour: MonoBehaviour {
void Start() {
StartCoroutine(GetText());
}
IEnumerator GetText() {
UnityWebRequest www = new UnityWebRequest("http://www.my-server.com");
www.downloadHandler = new DownloadHandlerBuffer();
yield return www.Send();
if(www.isError) {
Debug.Log(www.error);
}
else {
// Show results as text
Debug.Log(www.downloadHandler.text);
// Or retrieve results as binary data
byte[] results = www.downloadHandler.data;
}
}
}
DownloadHandlerBuffer
를 사용하여 이미지 파일을 다운로드한 다음 Texture.LoadImage
를 사용하여 원시 바이트에서 텍스처를 생성하는 대신 DownloadHandlerTexture
를 사용하는 것이 더 효율적입니다.
이 다운로드 핸들러는 수신한 데이터를 UnityEngine.Texture
에 저장합니다. 다운로드가 완료되면 JPEG와 PNG를 유효한 UnityEngine.Texture 오브젝트
로 디코드합니다. DownloadHandlerTexture
오브젝트마다 UnityEngine.Texture
복사본이 하나씩만 생성됩니다. 따라서 가비지 컬렉션으로 인한 성능 저하가 완화됩니다. 핸들러는 네이티브 코드로 버퍼링, 압축 풀기, 텍스처 생성을 수행합니다. 또한 압축 풀기 및 텍스처 생성은 메인 스레드 대신 워커 스레드에서 수행되므로 큰 텍스처를 로드할 때 프레임 시간이 개선됩니다.
마지막으로, DownloadHandlerTexture
는 텍스처 자체를 최종적으로 생성할 때만 관리되는 메모리를 할당하므로 스크립트에서 바이트를 텍스처로 변환하는 것과 관련된 가비지 컬렉션 오버헤드가 제거됩니다.
다음 예제에서는 인터넷에서 PNG를 다운로드하여 스프라이트로 변환하고 이미지에 할당합니다.
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Networking;
using System.Collections;
[RequireComponent(typeof(UnityEngine.UI.Image))]
public class ImageDownloader : MonoBehaviour {
UnityEngine.UI.Image _img;
void Start () {
_img = GetComponent<UnityEngine.UI.Image>();
Download("http://www.mysite.com/myimage.png");
}
public void Download(string url) {
StartCoroutine(LoadFromWeb(url));
}
IEnumerator LoadFromWeb(string url)
{
UnityWebRequest wr = new UnityWebRequest(url);
DownloadHandlerTexture texDl = new DownloadHandlerTexture(true);
wr.downloadHandler = texDl;
yield return wr.Send();
if(!wr.isError) {
Texture2D t = texDl.texture;
Sprite s = Sprite.Create(t, new Rect(0, 0, t.width, t.height),
Vector2.zero, 1f);
_img.sprite = s;
}
}
}
이 특화된 다운로드 핸들러의 장점은 데이터를 Unity 에디터의 에셋 번들 시스템에 스트리밍할 수 있다는 것입니다. 에셋 번들 시스템에 데이터가 충분히 수신되면 에셋 번들을 UnityEngine.AssetBundle
오브젝트로 사용할 수 있습니다. UnityEngine.AssetBundle
오브젝트 복사본 하나만 생성됩니다. 따라서 런타임 메모리 할당 및 에셋 번들 로드의 메모리 영향이 상당히 감소합니다. 또한 에셋 번들을 완전히 다운로드하지 않은 상태에서 부분적으로 사용할 수 있으므로 에셋도 스트리밍할 수 있습니다.
모든 다운로드와 압축 풀기는 워커 스레드에서 수행됩니다.
에셋 번들을 검색해서 가져오는 특수 assetBundle
프로퍼티가 있는 DownloadHandlerAssetBundle
오브젝트를 통해 에셋 번들이 다운로드됩니다.
에셋 번들 시스템의 작동 방식으로 인해 모든 에셋 번들에는 연결된 주소가 있어야 합니다. 일반적으로 이 주소는 에셋 번들이 있는 위치의 명목 URL(리디렉션하기 전의 URL을 의미)입니다. 거의 모든 경우 UnityWebRequest에 전달한 URL과 동일한 URL을 전달해야 합니다. 이 작업은 고수준 API(HLAPI)를 사용할 경우 자동으로 완료됩니다.
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
class MyBehaviour: MonoBehaviour {
void Start() {
StartCoroutine(GetAssetBundle());
}
IEnumerator GetAssetBundle() {
UnityWebRequest www = new UnityWebRequest("http://www.my-server.com");
DownloadHandlerAssetBundle handler = new DownloadHandlerAssetBundle(www.url, uint.MaxValue);
www.downloadHandler = handler;
yield return www.Send();
if(www.isError) {
Debug.Log(www.error);
}
else {
// Extracts AssetBundle
AssetBundle bundle = handler.assetBundle;
}
}
}
다운로드된 데이터의 처리를 완전히 관리해야 하는 사용자를 위해, Unity 에디터는 DownloadHandlerScript
클래스를 제공합니다.
기본적으로 이 클래스의 인스턴스는 아무것도 하지 않습니다. 하지만 DownloadHandlerScript
에서 자체 클래스를 가져오는 경우 특정 함수를 오버라이드하고 이를 사용하여 데이터가 네트워크에서 도착할 때 콜백을 수신할 수 있습니다.
참고: 실제 다운로드는 워커 스레드에서 발생하지만 모든 DownloadHandlerScript
콜백은 메인 스레드에서 작동합니다. 이 콜백 중에 연산 처리가 많은 작업은 피해야 합니다.
protected void ReceiveContentLength(long contentLength);
이 함수는 Content Length 헤더가 수신될 때 호출됩니다. UnityWebRequest를 처리하는 과정 중 서버가 하나 이상의 리디렉트 응답을 전송하는 경우 이 콜백이 여러 번 발생할 수도 있습니다.
protected void OnContentComplete();
이 함수는 UnityWebRequest가 서버에서 모든 데이터를 완전히 다운로드한 후 수신된 모든 데이터를 ReceiveData 콜백에 전달했을 때 호출됩니다.
protected bool ReceiveData(byte[] data, long dataLength);
이 함수는 원격 서버에서 데이터가 도착했을 때 프레임당 한 번씩 호출됩니다. data
인수에는 원격 서버에서 수신한 원시 바이트가 포함되며, dataLength
는 데이터 배열에서 새 데이터의 길이를 나타냅니다.
미리 할당된 데이터 버퍼를 사용하지 않는 경우 이 콜백을 호출할 때마다 시스템에서 새 바이트 배열을 생성하며, dataLength
는 항상 data.Length
와 같습니다. 미리 할당된 데이터 버퍼를 사용하는 경우 데이터 버퍼가 재사용되고, 업데이트된 바이트의 수를 찾으려면 dataLength
를 사용해야 합니다.
이 함수에는 true 또는 false 인 반환 값이 필요합니다. false 를 반환하면 시스템이 즉시 UnityWebRequest를 중단하고, true 를 반환하면 정상적으로 처리가 계속됩니다.
Unity 에디터의 고급 사용자는 대부분 가비지 컬렉션 때문에 발생하는 CPU 점유율이 치솟는 현상(CPU spike)를 줄이는 데 관심이 있습니다. 이러한 사용자를 위해 UnityWebRequest 시스템은 관리된 코드 바이트 배열을 사전 할당을 허용하며, 이 바이트 배열은 다운로드한 데이터를 DownloadHandlerScript의 ReceiveData
콜백에 전달하는 데 사용됩니다.
이 함수를 사용하면 다운로드한 데이터를 캡처하기 위해 DownloadHandlerScript에서 파생된 클래스를 사용할 때 관리되는 코드 메모리 할당을 완전히 제거할 수 있습니다.
DownloadHandlerScript
를 미리 할당된 관리 버퍼와 함께 사용하려면 바이트 배열을 DownloadHandlerScript
의 생성자에 공급해야 합니다.
참고: 바이트 배열의 크기에 따라 프레임마다 ReceiveData 콜백에 전달되는 데이터의 양이 제한됩니다. 너무 작은 바이트 배열을 제공하면 여러 프레임에 걸쳐 데이터가 늦게 도착할 수 있습니다.
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
public class LoggingDownloadHandler : DownloadHandlerScript {
// Standard scripted download handler - allocates memory on each ReceiveData callback
public LoggingDownloadHandler(): base() {
}
// Pre-allocated scripted download handler
// reuses the supplied byte array to deliver data.
// Eliminates memory allocation.
public LoggingDownloadHandler(byte[] buffer): base(buffer) {
}
// Required by DownloadHandler base class. Called when you address the 'bytes' property.
protected override byte[] GetData() { return null; }
// Called once per frame when data has been received from the network.
protected override bool ReceiveData(byte[] data, int dataLength) {
if(data == null || data.Length < 1) {
Debug.Log("LoggingDownloadHandler :: ReceiveData - received a null/empty buffer");
return false;
}
Debug.Log(string.Format("LoggingDownloadHandler :: ReceiveData - received {0} bytes", dataLength));
return true;
}
// Called when all data has been received from the server and delivered via ReceiveData.
protected override void CompleteContent() {
Debug.Log("LoggingDownloadHandler :: CompleteContent - DOWNLOAD COMPLETE!");
}
// Called when a Content-Length header is received from the server.
protected override void ReceiveContentLength(int contentLength) {
Debug.Log(string.Format("LoggingDownloadHandler :: ReceiveContentLength - length {0}", contentLength));
}
}