question

drmike avatar image
drmike asked

Unable to Create a Draft UGC Item

I'm new to Playfab, and trying to upload a test UGC item from within my game in Unity C#

Following this article:

https://docs.microsoft.com/en-us/gaming/playfab/features/commerce/ugc/publish-ugc

I've managed to successfully call CreateUploadUrls and have it return a sensible success response. I then take the settings in the response to create a CreateDraftItem call, and it fails with this error:

"One of the provided content or image URLs cannot be found in XForge storage. You must use content URLs created by XForge only. Correlation Id: KN3LEMxO2UWlAeGr+O8mSA.0"

I can make it give other sensible errors (if I say forget to pass the IDs through or specify a non-existent category), but I think I'm doing everything right here, and can't find any information online on this error.

On the Game Manager dashboard we have UGC enabled, players have permission to CreateUploadURLs and CreatDraftItems, and "Character" is set up as a UGC category.

Here's the code:

    public static void UploadCharacter(Character ch)
    {
        CreateUploadUrlsRequest uploadRequest = new CreateUploadUrlsRequest();
        uploadRequest.Files = new List<UploadInfo>();
        UploadInfo portraitInfo = new UploadInfo();
        portraitInfo.FileName = ch.characterNameAndID + ".png";
        uploadRequest.Files.Add(portraitInfo);
        UploadInfo charInfo = new UploadInfo();
        charInfo.FileName = ch.characterNameAndID + ".xml";
        uploadRequest.Files.Add(charInfo);
        uploadRequest.AuthenticationContext = PlayFabLogin.authContext;


        PlayFab.PlayFabEconomyAPI.CreateUploadUrls(uploadRequest, OnUploadSuccess, OnUploadFail, customData: ch.characterNameAndID);
    }


    static void OnUploadFail(PlayFabError error)
    {
        Debug.LogError(error.ErrorMessage);
    }


    static void OnUploadSuccess(CreateUploadUrlsResponse response)
    {
        string characterNameAndID = response.CustomData as string;
        CreateDraftItemRequest request = new CreateDraftItemRequest();
        request.Item = new PlayFab.EconomyModels.CatalogItem();
        request.Item.Type = "ugc";
        request.Item.Title = new Dictionary<string, string>();
        request.Item.Title["neutral"] = characterNameAndID;
        request.Item.Description = new Dictionary<string, string>();
        request.Item.Description["neutral"] = "Downloadable Test Character";
        request.Item.ContentType = "Character";
        request.Item.IsHidden = false;


        Content content = new Content();
        content.Url = response.UploadUrls[1].Url;
        content.Id = response.UploadUrls[1].Id;
        request.Item.Contents = new List<Content>();
        request.Item.Contents.Add(content);


        Image image = new Image();
        image.Type = "Thumbnail";
        image.Url = response.UploadUrls[0].Url;
        image.Id = response.UploadUrls[0].Id;


        request.Item.Images = new List<Image>();
        request.Item.Images.Add(image);


        request.AuthenticationContext = PlayFabLogin.authContext;


        request.Publish = true;


        PlayFab.PlayFabEconomyAPI.CreateDraftItem(request, UploadItemSuccess, UploadItemError);
    }


    static void UploadItemSuccess(CreateDraftItemResponse response)
    {
        Debug.LogError("Item uploaded correctly!");
    }


    static void UploadItemError(PlayFabError error)
    {
        Debug.LogError(error.ErrorMessage);
    }

Can anyone offer any tips or other help?

unity3d
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

drmike avatar image
drmike answered

I've got it! As you say, I wasn't uploading the files to the created Urls - I'd misread the tutorial thinking that was an optional step instead of using CreateDraftItem.

As I said, I'm new to this :)

I found some useful code in this post:

https://community.playfab.com/questions/58787/unable-to-upload-ugc-files.html

I added the upload step, and now its all working.

Thanks very much for pointing me in the right direction!

Here's the amended code, with an UploadFile function, and two calls to it prior to calling CreateDraftItem

    static void OnUploadSuccess(CreateUploadUrlsResponse response)
    {
        string characterNameAndID = response.CustomData as string;
        CreateDraftItemRequest request = new CreateDraftItemRequest();
        request.Item = new PlayFab.EconomyModels.CatalogItem();
        request.Item.Type = "ugc";
        request.Item.Title = new Dictionary<string, string>();
        request.Item.Title["neutral"] = characterNameAndID;
        request.Item.Description = new Dictionary<string, string>();
        request.Item.Description["neutral"] = "Downloadable Test Character";
        request.Item.ContentType = "Character";
        request.Item.IsHidden = false;


        Content content = new Content();
        content.Url = response.UploadUrls[1].Url;
        content.Id = response.UploadUrls[1].Id;
        request.Item.Contents = new List<Content>() { content };


        Image image = new Image();
        image.Type = "Thumbnail";
        image.Url = response.UploadUrls[0].Url;
        image.Id = response.UploadUrls[0].Id;
        request.Item.Images = new List<Image>() { image };


        UploadFile(content.Url, characterNameAndID + ".xml", false);
        UploadFile(image.Url, characterNameAndID + ".png", true);


        request.Publish = true;


        PlayFab.PlayFabEconomyAPI.CreateDraftItem(request, UploadItemSuccess, UploadItemError);
    }


    static void UploadFile(string url, string filePath, bool isImage)
    {
        try
        {
            using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read);


            var request = (HttpWebRequest)WebRequest.Create(url);
            request.Method = "PUT";
            request.ContentType = isImage ? "image/jpeg" : "text/plain";
            request.ContentLength = stream.Length;
            request.Timeout = 1 * 60 * 1000;
            request.ReadWriteTimeout = 1 * 60 * 1000;
            request.Headers.Add("comp", "blob");
            request.Headers.Add("x-ms-blob-type", "blockblob");


            using var requestStream = request.GetRequestStream();
            stream.CopyTo(requestStream);


            var response = request.GetResponse();


            Debug.Log("Upload Success: " + filePath);
        }
        catch (Exception e)
        {
            Debug.Log($"Error : {e.StackTrace}");
        }
    }
1 comment
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Made Wang avatar image Made Wang commented ·

I'm glad you solved this issue.

0 Likes 0 ·
Made Wang avatar image
Made Wang answered

After testing, if you call CreateDraftItem directly without uploading the file after calling CreateUploadUrls, the error you said will appear. Did you upload the file before calling CreateDraftItem?

Refer to Publish your first user generated content - PlayFab | Microsoft Docs to upload your files via Postman or AzCopy.

10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

drmike avatar image
drmike answered

Yeah, the files are uploaded first.

I call UploadCharacter, which assembles a UploadRequest and calls CreateUploadUrls.

That then calls OnUploadSuccess when it returns, which assembles a CreateDraftItemRequest and call CreateDraftItem.

That call then fails with the error message about XForge urls.

One thing I've tried since the original post is to remove the token from the urls in the CreateUploadUrlsResponse, since response.UploadUrls[0].Url is a url + '?' + token.

content.Url = GetURL(response.UploadUrls[1].Url);

with

static string GetURL(string urlPlusToken)
{
    int indexOfQuery = urlPlusToken.IndexOf('?');
    return indexOfQuery > 0 ? urlPlusToken.Substring(0, indexOfQuery) : urlPlusToken;
}

I still get the same error message then though, so I dont think that was the problem.

1 comment
10 |1200

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Made Wang avatar image Made Wang commented ·

To clarify, CreateUploadUrls does not upload files, this API just creates urls. You need to manually upload the file to the created url, you can do it via Postman or AzCopy mentioned above.

0 Likes 0 ·

Write an Answer

Hint: Notify or tag a user in this post by typing @username.

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.