11

I'm trying to create a copy of an existing component using the core service which has worked fine up until I hit a binary component when running my console app.

To give you an example of what my code is doing you can see below that I check .BinaryContent and then try to build up the properties before creating the component.

public static string Create(CoreServiceClient client, string componentId)
{
var compData = (ComponentData)client.Read(componentId, new ReadOptions());

var component = (ComponentData)client.GetDefaultData(ItemType.Component, compData.LocationInfo.OrganizationalItem.IdRef);
component.Id = "tcm:0-0-0";
component.Title = compData.Title;
component.Schema.IdRef = compData.Schema.IdRef;
component.Content = compData.Content;
component.Metadata = compData.Metadata

if (compData.BinaryContent != null)
{
    component.BinaryContent = new BinaryContentData
    {
        UploadFromFile = compData.BinaryContent.UploadFromFile,
        Filename = compData.BinaryContent.Filename,
        MultimediaType = new LinkToMultimediaTypeData()
        {
            IdRef = compData.BinaryContent.MultimediaType.IdRef
        }
    };
}

try
{
    component = (ComponentData)client.Create(component, new ReadOptions());
}
catch (Exception ex)
{
    _log.ErrorFormat("Unable to create component: {0}", ex);
}

return component.Id;
}

However, I'm getting the following error:

System.ServiceModel.FaultException`1[BlueprintTool.Data.CoreService.CoreServiceFault]: Invalid value for property 'BinaryContent'. Neither value for UploadFromFile property nor UploadFromStream is provided for item (Fault Detail is equal to BlueprintTool.Data.CoreService.CoreServiceFault).

Do I need to provide a path for the binary? Can anyone see where I am going wrong?

Thanks

UPDATE

OK thanks for the comments I've updated my code to show you what I have now including the changes to the location of the component. Please see below:

    public static string Create(CoreServiceClient client, string componentId, string targetComponentLocationId)
{
    var compData = (ComponentData)client.Read(componentId, new ReadOptions());

    // Start creating component
    var component = (ComponentData)client.GetDefaultData(ItemType.Component, compData.LocationInfo.OrganizationalItem.IdRef);
    component.Id = "tcm:0-0-0";
    component.Title = compData.Title;
    component.Schema.IdRef = compData.Schema.IdRef;
    component.Content = compData.Content;

    if (!String.IsNullOrEmpty(compData.Metadata))
    {
        component.Metadata = compData.Metadata;
    }

    // Check and create binary component
    if (compData.BinaryContent != null)
    {
        string origFilename = compData.BinaryContent.Filename;
        string extension = origFilename.Substring(origFilename.LastIndexOf('.') + 1);
        string tempPath = string.Empty;

        using (var suClient = new StreamUploadClient("streamUpload_basicHttp"))
        {
            using (var sdClient = new StreamDownloadClient("streamDownload_basicHttp"))
            {
                string file = "C:\\MyTempLocation\\" + Path.GetFileName(compData.BinaryContent.Filename);

                using (var fs = File.Create(file))
                {
                    byte[] binaryContent = null;

                    var tempStream = sdClient.DownloadBinaryContent(compData.Id);
                    var memoryStream = new MemoryStream();
                    tempStream.CopyTo(memoryStream);
                    binaryContent = memoryStream.ToArray();

                    tempPath = suClient.UploadBinaryContent(origFilename, memoryStream);
                }
            }
        }

        // Find multimedia type
        var list = client.GetSystemWideList(new MultimediaTypesFilterData());
        var multimediaType = list.OfType<MultimediaTypeData>().Single(mt => mt.FileExtensions.Contains(extension));

        // Set binary content of component
        component.BinaryContent = new BinaryContentData
        {
            UploadFromFile = tempPath,
            Filename = origFilename,
            MultimediaType = new LinkToMultimediaTypeData { IdRef = multimediaType.Id }
        };
    }

    component.LocationInfo = new LocationInfo
    {
        OrganizationalItem = new LinkToOrganizationalItemData
        {
            IdRef = targetComponentLocationId
        }
    };

    try
    {
        component = (ComponentData)client.Create(component, new ReadOptions());
    }
    catch (Exception ex)
    {
        _log.ErrorFormat("Unable to create component: {0}", ex);
    }

    return component.Id;
}

When I run this it does work but when I go into Tridion and open up the binary component I see the following message in the preview

HTTP 404: File not found, reverting to mimetype

I'm pretty sure I've seen Tridion do this before but even when I go to C:\\MyTempLocation\\ I try opening the binary and its empty.

Can anyone see a problem with my approach here?

  • What does compData.BinaryContent.UploadFromFile have when you run this against a specific component? Any chance that the selected component isn't a multimedia component? – Alvin Reyes May 1 '13 at 14:26
  • Thanks Alvin. It's definitely a multimedia component as 'MultimediaType' isn't null and I've also checked the component in Tridion. 'compData.BinaryContent.UploadFromFile' is null. – Ibrar Hussain May 1 '13 at 14:43
12

The reason you are seeing this error is because you are creating a new Multimedia Component using the Create method. When creating a new multimedia component, it looks at the BinaryContent.UploadFromFile property to upload the binary from a file path. If you look at your compData.BinaryContent.UploadFromFile property, its actually not set, so you are just copying a null value over, thus your property not set error.

Since you are copying components, have you thought of using the Copy method?

var component = _client.Copy(compData.Id, compData.LocationInfo.OrganizationalItem.IdRef, true, new ReadOptions());

You can then make any changes to the component if needed and save those changes.

Update: Since you are copying across publications and can't use the Copy method as per your comment, you could probably do something like download the binary from the original component, and then set the UploadFromFile to the path of the downloaded binary. The code will look something along the lines of:

var compData = (ComponentData)client.Read(componentID, new ReadOptions());

var component = ...... // all your component creation stuff here...

if (compData.BinaryContent != null)
{
    string filePath = @"C:\TempFiles\" + Path.GetFileName(compData.BinaryContent.FileName);
    StreamDownloadClient downloadClient = new StreamDownloadClient("streamDownload_netTcp_2011");

    using (Stream stream = downloadClient.DownloadBinaryContent(compData.Id))
    {
        using (FileStream fs = File.Create(filePath))
        {
            using (MemoryStream ms = new MemoryStream())
            {
                stream.CopyTo(ms);
                byte[] binary = ms.ToArray();
                fs.Write(binary, 0, binary.Length);
            }
        }
    }
    component.BinaryContent = new BinaryContentData
    {
        Filename = compData.BinaryContent.Filename,
        MultimediaType = compData.BinaryContent.MultimediaType,
        UploadFromFile = filePath
    }
}
component = (ComponentData)client.Create(component, new ReadOptions());

Note that the above assumes your code is running on the CMS Server. If you are running the code remotely, you'll have to upload the file using StreamUploadClient after downloading it, as in Bart's example. If your code is running on the CMS Server, then there's no need to upload, as your binary file will be saved on the server already during the download.

Hope that helps!

  • Hi Alex, thanks for your answer. I would have used the Copy method but I'm copying to a new publication and Tridion doesn't support cross publication copying. – Ibrar Hussain May 1 '13 at 14:38
  • @IbrarHussain you menton you are copying to a new Publication, but in your code you use compData.LocationInfo.OrganizationalItem.IdRef for the location, which would create the new Component in the same location as your original – Bart Koopman May 1 '13 at 16:00
  • Thanks Bart, yes at the moment the location is still the same but I will be doing a .Replace() to change the IdRef – Ibrar Hussain May 1 '13 at 16:25
  • Note: you can't impersonate StreamDownloadClient. Instead use DownloadBinaryContentByUser() method and pass in client.GetCurrentUser() after impersonating. (Thanks to Alex on a midnight debugging session). – Nickoli Roussakov Jun 10 '15 at 1:31
10

The image uploader in the SDL Tridion Powertools project has an example of uploading / creating binaries using the core service:

FileInfo fileInfo = new FileInfo(file);
if (fileInfo.Exists)
{
        string mmType = GetMultiMediaType(fileInfo.Extension);
        if (mmType != null)
        {
                BinaryContentData bcd = new BinaryContentData
                {
                        UploadFromFile = file,
                        MultimediaType = new LinkToMultimediaTypeData { IdRef = mmType },
                        Filename = file,
                        IsExternal = false
                };

                ComponentData compData = new ComponentData
                {
                        LocationInfo = new LocationInfo
                        {
                                OrganizationalItem = new LinkToOrganizationalItemData
                                {
                                        IdRef = parameters.FolderUri //Organizational item
                                },
                        },
                        ComponentType = ComponentType.Multimedia,
                        Title = MakeValidFileName(fileInfo.Name),

                        Schema = new LinkToSchemaData
                        {
                                IdRef = parameters.SchemaUri //schemaData.IdRef
                        },

                        IsBasedOnMandatorySchema = false,
                        IsBasedOnTridionWebSchema = true,
                        ApprovalStatus = new LinkToApprovalStatusData
                        {
                                IdRef = "tcm:0-0-0"
                        },
                        Id = "tcm:0-0-0",
                        BinaryContent = bcd
                };

                ComponentData comp = (ComponentData)_client.Create(compData, new ReadOptions());
        }
}
}

NOTE: The code contains references to things like folder and schema Id's - which you'll need to be aware of - but i think the example is enough to help you solve your problem, if it helps you can check out the full code here:

https://code.google.com/p/tridion-2011-power-tools/source/browse/trunk/PowerTools.Model/Services/ImageUploader.svc.cs

  • Thanks John. I'll have a look at this and see if it helps me get to the bottom of the problem. – Ibrar Hussain May 1 '13 at 14:45
6

To create a Multimedia Component you will first need to upload the binary data to the Content Manager, and then you create the Component and set its Multimedia Properties. See my sample code below:

string myFilename = "picture.jpg";  // you can get this from the original component
string extension = myFilename.Substring(myFilename.LastIndexOf('.') + 1);
string tempPath;

// upload binary content to cms 
using (StreamUploadClient suClient = new StreamUploadClient("streamUpload_netTcp_2012"))
{
    // use the content stream from your original component 
    // by creating a new MemoryStream using a byte[] for example
    tempPath = suClient.UploadBinaryContent(myFilename, myBinaryContentStream);
}

// create tcm item
var mmComponent = new ComponentData
{
    Id = "tcm:0-0-0",
    Title = myTitle,
    Schema = new LinkToSchemaData { IdRef = "tcm:1-22-8" },
    LocationInfo = new LocationInfo { OrganizationalItem = new LinkToOrganizationalItemData { IdRef = "tcm:1-33-2" } }
};

// put binary data in tcm item
using (SessionAwareCoreServiceClient client = new SessionAwareCoreServiceClient("netTcp_2012"))
{
    // find multimedia type
    var list = client.GetSystemWideList(new MultimediaTypesFilterData());
    var multimediaType = list.OfType<MultimediaTypeData>().Single(mt => mt.FileExtensions.Contains(extension));

    // set BinaryContent of a component
    mmComponent.BinaryContent = new BinaryContentData
    {
        UploadFromFile = tempPath,
        Filename = myFilename,
        MultimediaType = new LinkToMultimediaTypeData { IdRef = multimediaType.Id }
    };

    // create (and save) component
    ComponentData data = (ComponentData)client.Create(mmComponent, new ReadOptions());
    string uriAfterSave = data.Id;
}
  • Thanks Bart, ill give this a shot and let you know how I get on. – Ibrar Hussain May 1 '13 at 16:27
5

I've managed to figure out the problem. I didn't need to download the file to a specific location I can just use the existing stream item. See code below:

                if (compData.BinaryContent != null)
            {
                string origFilename = compData.BinaryContent.Filename;
                string extension = origFilename.Substring(origFilename.LastIndexOf('.') + 1);
                string tempPath = string.Empty;

                // Upload binary content to cms 
                using (var suClient = new StreamUploadClient("streamUpload_basicHttp"))
                using (var sdClient = new StreamDownloadClient("streamDownload_basicHttp"))
                using (var tempStream = sdClient.DownloadBinaryContent(compData.Id))
                {
                    // Uses the content stream from original component 
                    tempPath = suClient.UploadBinaryContent(origFilename, tempStream);
                }

                // Find multimedia type
                var list = client.GetSystemWideList(new MultimediaTypesFilterData());
                var multimediaType = list.OfType<MultimediaTypeData>().Single(mt => mt.FileExtensions.Contains(extension));

                // Set binary content of component
                component.BinaryContent = new BinaryContentData
                {
                    UploadFromFile = tempPath,
                    Filename = origFilename,
                    MultimediaType = new LinkToMultimediaTypeData { IdRef = multimediaType.Id }
                };
            }

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.