This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

REST API - Updating an existing file

Good evening everyone,

let me introduce you to my problem with updating existing files and kindly ask you for a fix suggestion :)

You will probably notice that I am using an older REST-like API called MFWSClient.
A newer version was published on GitHub: https://github.com/M-Files/Libraries.MFWSClient on [color=blue]28.3.2020[/color], but due to reasons and hope this version will not be deprecated, I stick with it.

I have two questions. You will notice.

As the M-Files Developer portal similarly states on: https://developer.m-files.com/APIs/REST-API/Updating-Objects/#updating-an-existing-file
if one desires to update an existing file, the following should be done:

1. Authenticate: (my code below)

client = new MFClient(pURL);
client.Authentication = client.Post<PrimitiveType<string>>("/server/authenticationtokens", new Authentication { Username = pUsername, Password = pPassword, VaultGuid = pVaultGUID }).Value;
client.Authentication = client.Get<Vault>("/session/vault").Authentication;

Thanks to [color=green]SessionInfo[/color] I am able to check the user related information:

SessionInfo sessionInfo = client.Get<SessionInfo>("/session");
/*
Console.WriteLine("THE VALUES LISTED BELOW");

// Console output
sessionInfo.AccountName = ADMIN
sessionInfo.ACLMode = AutomaticPermissionsWithComponents
sessionInfo.AuthenticationType = SpecificMFilesUser
sessionInfo.CanForceUndoCheckout = True
sessionInfo.CanManageCommonUISettings = True
sessionInfo.CanManageCommonViews = True
sessionInfo.CanManageTraditionalFolders = True
sessionInfo.CanMaterializeViews = True
sessionInfo.CanSeeAllObjects = True
sessionInfo.CanSeeDeletedObjects = True
sessionInfo.InternalUser = True
sessionInfo.LicenseAllowsModifications = True
sessionInfo.UserID = 9
*/


It seems like I should be on the correct account as well as have the permissions to perform the desired activity.  :)

2. Check out an existing object: (my code below)

ObjectVersion objectVersion = client.Put<ObjectVersion>(string.Format("/objects/{0}/{1}/{2}/checkedout", pType, pObjectID, pVersion),
                                                                                  new PrimitiveType<MFCheckOutStatus>() { Value = MFCheckOutStatus.CheckOutToMe });

Thanks to [color=green]ObjectVersion[/color] I am able to check the object related information:

ObjectVersion objectVersion = client.Get<ObjectVersion>(string.Format("/objects/{0}/{1}/{2}", pType, pObjectID, pVersion));
/*
Console.WriteLine("THE VALUES LISTED BELOW");

// Console output
objectVersion.CheckedOutToUserName = Administrator
objectVersion.CheckedOutTo = 9
objectVersion.ObjectCheckedOut = True
objectVersion.ObjectCheckedOutToThisUser = True
*/

Looking at the console, it seems like the object was checked out to me.  :)
[color=red]But looking at M-Files Explorer it is apparently not - Please see the image:[/color] https://imgur.com/MUlhhIg

Question #1. What did I do wrong here?

3. Prepare and update / upload a file: (my code below)

ObjectFile[] files = client.Get<ObjectFile[]>(string.Format("/objects/{0}/{1}/{2}/files", pType, pObjectID, pVersion));
if (files.Length == 0) return;
/*
DownloadFileAsStream(); <- I was able to extract the code for this from the new MFWSClient API. The older one did not provide this functionality.
ConvertFileToReadableFormat();
SaveFileOnHardDrive();
OpenAndEditFile();
*/

ObjectVersion objectVersion = client.Put<ObjectVersion>(string.Format("/objects/{0}/{1}/{2}/files/{3}/content", pType, pObjectID, pVersion, files[0].ID),
                                                    new System.Net.Http.StreamContent(new FileInfo(Path.GetFullPath(pFilePath)).OpenRead()));


I am aware the last REST-like call might not be entirely correct,
but I did my best to search for the right examples on the M-Files Documentation sources,
but I have to say that some of them are missing or not matching the API publised on GitHub.
I apologize if I missed anything and will be grateful for a hint on this one.

Question #2. What is the correct way?
(I see a lot of forums that mention: "application/octet-stream" with posting random binary formatfiles across the interwebs)
(I also noticed the client.Put<T>(); method originally does not account for this)

4. Check back in (not important in this context)

Thank you a lot M-Files staff & fellow developers, perhaps this post is useful to other newcomers as well.

Edit:
This is the method performing the GET / PUT requests:


public T Get<T>(string pPath) {
    return (T)PerformRequest("GET", pPath, null, typeof(T));
}
public T Put<T>(string pPath, object pContent) {
    return (T)PerformRequest("PUT", pPath, pContent, typeof(T));
}
private object PerformRequest(string pMethod, string pPath, object pContent, Type pReturnType) {
    if (pPath[0] == '/')
        pPath = pPath.Substring(1);
        string uri = EndPoint + pPath;
        if (pMethod == "PUT" || pMethod == "DELETE") {
            if (uri.Contains("?")) uri += "&";
            else uri += "?";
            uri += "_method=" + pMethod;
        }
        WebRequest request = WebRequest.Create(uri);
        request.Method = (pMethod.ToUpper() == "GET") ? "GET" : "POST";
        if (!string.IsNullOrEmpty(Authentication)) request.Headers["X-Authentication"] = Authentication;
        if (pContent != null) {
            if (pContent is Stream) {
                Stream requestStream = request.GetRequestStream();
                Stream sourceStream = (Stream)pContent;
                sourceStream.CopyTo(requestStream);
                requestStream.Flush();
                requestStream.Close();
            } else {
                Stream requestStream = request.GetRequestStream();
                DataContractJsonSerializer requestSerializer = new DataContractJsonSerializer(pContent.GetType());
                requestSerializer.WriteObject(requestStream, pContent);
                requestStream.Flush();
                requestStream.Close();
            }
        }
        WebResponse response;
        try {
            response = request.GetResponse();
        } catch (WebException pException) {
            HandleError(pException);
            throw;
        }
        if (pReturnType == null) return null;
        if (pReturnType == typeof(string)) return new StreamReader(response.GetResponseStream()).ReadToEnd();
        if (pReturnType == typeof(Stream)) return response.GetResponseStream();
        if (response.GetResponseStream() == null) return null;
        return new DataContractJsonSerializer(pReturnType).ReadObject(responseStream);
}
Parents
  • Hi,

    So, firstly, I would be very interested to hear the reasons not to move to that library. The whole purpose of it is to try and hide the vast majority of the complexity associated with interacting with the REST API, so it may well help with getting these things working.

    However, let's look at those questions.

    Question #1. What did I do wrong here?

    The screenshot you shared shows that the object is indeed checked out to you, via the REST API. A checkout is computer-specific (if you checked out an object on computer A, then accessed M-Files from computer B then it would show as checked out by someone else). So this looks fine.

    Question #2. What is the correct way?

    What you have posted seems to be the correct way. Basically the binary data of the file needs to be in the request body and, provided you then hit the correct endpoint with the correct HTTP headers and verb, it should work. I guess that you should also set the content type request header to the correct type for your file (or "application/octet-stream", if you want something generic); I'm not sure what the HttpClient will do for you automatically in this instance. You neglect to say what actual issue you're encountering here - do you get an error?

    Once you've made your "update" request, you need to then check the object back in so that you can see the file changes. Have you done that? Can you see the updated file?

    Regards,

    Craig.
Reply
  • Hi,

    So, firstly, I would be very interested to hear the reasons not to move to that library. The whole purpose of it is to try and hide the vast majority of the complexity associated with interacting with the REST API, so it may well help with getting these things working.

    However, let's look at those questions.

    Question #1. What did I do wrong here?

    The screenshot you shared shows that the object is indeed checked out to you, via the REST API. A checkout is computer-specific (if you checked out an object on computer A, then accessed M-Files from computer B then it would show as checked out by someone else). So this looks fine.

    Question #2. What is the correct way?

    What you have posted seems to be the correct way. Basically the binary data of the file needs to be in the request body and, provided you then hit the correct endpoint with the correct HTTP headers and verb, it should work. I guess that you should also set the content type request header to the correct type for your file (or "application/octet-stream", if you want something generic); I'm not sure what the HttpClient will do for you automatically in this instance. You neglect to say what actual issue you're encountering here - do you get an error?

    Once you've made your "update" request, you need to then check the object back in so that you can see the file changes. Have you done that? Can you see the updated file?

    Regards,

    Craig.
Children
No Data