Issue accessing files with cfs upload via API

I'm using the CFS temporary endpoint community.telligent.com/.../upload-cfs-rest-endpoint to upload a file and then passing the url of the file to the create media post endpoint.

I've used some of the code from here as a basis; https://community.telligent.com/community/9/w/developer-training/53237/uploading-files#Embed_a_File_in_a_Post

However I'm having issues when trying to view the files as other users.

I have tried to upload the files as the admin user and create the posts as an admin user and also via impersonation using other user accounts and unless I create and upload the files under my account I can't view/download them. I get an 'you do not have permissions to view the directory or page' error message.

I can see the post in the media gallery fine, just not the file.

There is something simliar mentioned here: https://community.telligent.com/community/10/f/ask-the-community/1145434/issue-using-files-uploaded-to-cfs-via-rest/1600829#1600829 but I've tried everything I can think of and I still can't get the files to be viewable/downloadable by group members of the gallery. I've checked the gallery permissions too and they seem fine.

Can someone help me out with this?

Parents Reply Children
  • Hi, no problem. here is is (it's all based on the sample code provided in the links above and that code uses the url, not the contexid I think).

     public string UploadFile(string restUserToken, string url, Guid uploadContextId, string filePath, string impersonateUser = null)
            {
                string fileUrl = null;
    
                try
                {    
                    FileInfo file = new FileInfo(filePath);
                    // Name file variables
                    var fileName = file.Name;
                    var fileContentType = MimeMapping.GetMimeMapping(file.FullName);
                    var fileDataStream = file.OpenRead();
    
                    // Calculate chunk information
                    int totalChunks = (int) Math.Ceiling((double) fileDataStream.Length / MAX_CHUNK_SIZE_BYTES);
    
                    // Open the file reader to read chunks
                    using (var rdr = new BinaryReader(fileDataStream))
                    {
                        // Send each chunk in succession
                        for (var currentChunk = 0; currentChunk < totalChunks; currentChunk++)
                        {
     
                            var boundary = Guid.NewGuid().ToString("N");
    
                            // Create request
                            var request = (HttpWebRequest) WebRequest.Create(url + "/api.ashx/v2/cfs/temporary.json");
                            request.Headers.Add("Rest-User-Token", restUserToken);
                            if (!string.IsNullOrEmpty(impersonateUser))
                            {
                                request.Headers.Add("Rest-Impersonate-User", impersonateUser);
                            }
                            request.ContentType = "multipart/form-data; boundary=" + boundary;
    
                            // Collect necessary request information
                            var formData = new Dictionary<string, string>
                            {
                                {"UploadContextId", uploadContextId.ToString()},
                                {"FileName", fileName},
                                {"CurrentChunk", currentChunk.ToString()},
                                {"TotalChunks", totalChunks.ToString()},
                            };
    
                            // Add data to the multi-part form
                            var requestData = new StringBuilder();
                            foreach (var item in formData)
                            {
                                requestData.Append(
                                    $"--{boundary}\r\nContent-Disposition: form-data; name=\"{item.Key}\"\r\n\r\n{item.Value}\r\n");
                            }
    
                            // Add the file itself
                            requestData.Append(
                                $"--{boundary}\r\nContent-Disposition: form-data; name=\"file\"; filename=\"{fileName}\"\r\nContent-Type: {fileContentType}\r\n\r\n");
    
                            // Prepare data chunk
                            var startDataBytes = Encoding.UTF8.GetBytes(requestData.ToString());
                            var chunk = rdr.ReadBytes(MAX_CHUNK_SIZE_BYTES);
                            var endDataBytes = Encoding.UTF8.GetBytes($"\r\n--{boundary}--\r\n");
    
                            // Combine all the request data into one array
                            var bytesToSend = new byte[startDataBytes.Length + chunk.Length + endDataBytes.Length];
                            startDataBytes.CopyTo(bytesToSend, 0);
                            chunk.CopyTo(bytesToSend, startDataBytes.Length);
                            endDataBytes.CopyTo(bytesToSend, startDataBytes.Length + chunk.Length);
    
                            try
                            {
                                // Make request and read results
                                var response = SendPostRequest(request, bytesToSend);
    
                                if (response?["Errors"] != null)
                                {
                                    return string.Join("\r\n", response["Errors"]);
                                }
    
                                fileUrl = response?["UploadedFile"]?["DownloadUrl"];
                            }
                            catch (WebException ex)
                            {
                                using (var stream = ex.Response.GetResponseStream())
                                using (var reader = new StreamReader(stream))
                                {
                                    return "Error: " + reader.ReadToEnd();
                                }
                            }
                            catch (Exception ex)
                            {
                                return InvestigateError(ex);
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    return "Error: " ex.Message;
                }
    
                return fileUrl;
            }

    Thanks for your help with this.

  • What error do you get when you create the media post without the FileName and ContentType?

  • Without Filename an ContentType I get:

    'A file name must be included when attaching a file'

    Then with Filename but no Content type I get:

    'The file's content type must be provided when attaching a file'

    and if i include them both I get the error above about 'the File with identifier x' could not be found'

  • I've logged the issue with the documentation.  FileName and ContentType are required even when passing in a FileUploadContext. 

    For the FileName, can you try passing in the actual filename, not the encoded filename you're getting from the URL?

    106834

  • Thanks Brian. It was very confusing for me to be honest. I've tried using the filename without encoding it and it still doesn't find it the file. I've tried all the formats I can think of, even encoding it myself as well as grabbing it from the temp file url (as per the code). If you drop these two bits of code into a web form project, add the functions above and run it you should see the same issues:

     <form id="form1" runat="server">
        <div>
    		file: <asp:FileUpload runat="server" id="fuFile"/>
    		<br/>
    		url: <asp:TextBox runat="server" id="tbUrl"></asp:TextBox>
    		<br/>
    		api username:<asp:TextBox runat="server" id="tbApiKeyName"></asp:TextBox>
    		<br/>
            api key:<asp:TextBox runat="server" id="tbApiKey"></asp:TextBox>
    		<br/>
    		<asp:Button runat="server" id="btnSubmit" Text="Submit"/>
    		<br/>
    		<asp:Label runat="server" id="lblInfo"></asp:Label>
        </div>
        </form>


                // Calculate REST token
    			var apiKey = tbApiKey.Text;
    			var apiKeyName = tbApiKeyName.Text;
    		    var folderPath = @"C:\telligent\downloads";
    
    
                var restUserToken = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{apiKey}:{apiKeyName}"));
    
    			var uploadContextId = Guid.NewGuid();
    		    var uploadFileExtensions = new TelligentUploadExtensions.UploadExtensions();
    
                var dir = new DirectoryInfo(folderPath);
    		    FileInfo[] files = dir.GetFiles();
    		    for (int i = 0; i < files.Length; i++)
    		    {
    		        FileInfo file = files[i];
    		        var uploadUrl = uploadFileExtensions.UploadFile(restUserToken, url, uploadContextId, file.FullName);
    		        var postUrl = uploadFileExtensions.CreateMediaPost(restUserToken, url, uploadContextId, file.FullName, uploadUrl, 1, "testuser");
                    lblInfo.Text += $"{uploadContextId}<br />{uploadUrl}<br /><br />{postUrl}";
                }

  • I think I see what may be the issue.  When calling uploadFileExtensions.Uploadfile, you're not impersonating the same user who is creating the media post.  The temporary upload is secured by the contextual user, same user needs to be impersonated when uploading the file as when creating the media post.

  • Hi Brian, I thought I'd tried without impersonation and with impersonation and neither worked but I'll give it another go. Do you think it should be the filename encoded or not encoded that I need to use?

  • Hi Brian. I've tested this and it does seem to work now but I did have to include the ContentType as well else I got an error. Therefore please can you update the docs to reflect this. Also I think you could really do with some more in-depth examples to assist with this and cover the scenario where users are being impersonated as I'm sure I'm not the only one who's been caught out by this. Thanks for your help.

  • Glad you got it working.  We will get the docs updated.