• RE: DocuWare Platform .NET API - Checking Documents In/Out

    Edward,

    Some other async examples make the operation become synchronous by asking for the Result of the operation. Consider the example of downloading a document, found here:

    https://developer.docuware.com/dotNet_CodeExamples/4354370d-a0ce-42d4-ba94-6dc848ed62c0.html

    The main call is an async method, but the Result is summoned, making the program wait:

                var downloadResponse = document.PostToFileDownloadRelationForStreamAsync(
                    new FileDownload()
                    {
                        TargetFileType = FileDownloadType.Auto
                    }).Result;


    This gives you back the stream immediately for download.

    I am not sure other async routines are written in such a way that this will always work, and async stuff makes my head hurt.

    A really quick and dirty way to know if the file is done downloading would be to wait for the file to exist, and then try to lock it for exclusive write. That shouldn't be allowed while the file is still downloading. I have a WaitForFile routine I use for that:
     

            /// <summary>
            /// Like the global VFP function of the same name, this routine waits for a file to appear and not be in use elsewhere (via exclusive write check)
            /// so that the file is known to be available for use and no longer being filled or operated on by any other process.
            /// </summary>
            /// <param name="fileName">The file name to wait for.</param>
            /// <param name="timeout">Milliseconds to wait for the file (forever, by default).</param>
            /// <param name="silent">If true, do not display any information to use, not even if timeout is hit.</param>
            /// <param name="prePauseSeconds">A time to pause (in seconds) before checking for the file, to give the file time to appear regardless of timeout.</param>
            /// <returns>Returns TRUE if file is found and free of other use within timeout period, otherwise FALSE.</returns>
            public static bool WaitForFile(string fileName, int timeout = Int32.MaxValue, bool silent = false, int prePauseSeconds = 0)
            {
                bool fileFound = false;
                int numLoops = 0;
                if (fileName.Length == 0) { return false; }
                if (timeout <= 0)
                {
                    // A timeout has been passed in but is zero or less. Assume this is meant to be an infinite wait.
                    timeout = Int32.MaxValue;
                }
                if (prePauseSeconds > 0)
                {
                    System.Threading.Thread.Sleep(prePauseSeconds * 1000);
                }
                // We can now wait for the file to appear.
                while ((!fileFound) && (numLoops <= timeout))
                {
                    if (File.Exists(fileName))
                    {
                        fileFound = true;
                    }
                    else
                    {
                        // Wait one second before trying again...
                        System.Threading.Thread.Sleep(1000);
                        Application.DoEvents();
                        if ((numLoops >= 5) && (!silent))
                        {
                            UIFuncs.StartWait("Waiting for file " + fileName + " " + numLoops.ToString() + " times...");
                        }
                        numLoops++;
                    }
                }
                // If file not found, then we have timed out.
                if (!fileFound)
                {
                    if (!silent) { UIFuncs.StopWait(); }
                    return false;
                }
                // If we make it here, we have file, but it might still be growing or in use.
                // Wait until nothing else has it in use.
                numLoops = 0;
                while (FileInUse(fileName) && (numLoops <= timeout))
                {
                    // Wait one second before trying again...
                    System.Threading.Thread.Sleep(1000);
                    Application.DoEvents();
                    if ((numLoops >= 5) && (!silent))
                    {
                        UIFuncs.StartWait("Waiting for file " + fileName + " " + numLoops.ToString() + " times...");
                    }
                    numLoops++;
                }
                if (!silent) { UIFuncs.StopWait(); }
                return (numLoops <= timeout);
            }
    


    Never mind the "StartWait" stuff, that is just a popup display I developed to mimic FoxPro's WAIT WINDOW functionality. The above routine calls the method FileInUse, which looks like this:

            /// <summary>
            /// Check to se if a file is in use by trying to open it with exlusive write access (that lock is removed if access is granted).
            /// </summary>
            /// <param name="fileName">The file name to check for being in use.</param>
            /// <returns>Returns TRUE if the file is in use elsewhere, FALSE otherwise.</returns>
            public static bool FileInUse(string fileName)
            {
                bool inUse = true;
                FileStream stream = null;
                try
                {
                    FileInfo fi = new FileInfo(fileName);
                    stream = fi.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None);
                    inUse = false;
                }
                catch (IOException)
                {
                    // File is in use by another process.
                    inUse = true;
                }
                finally
                {
                    if (stream != null) { stream.Close(); }
                }
                return inUse;
            }
    


    Maybe that would work for you, assuming you are doing this in C#/.NET, of course.

    Thanks,
    Joe Kaufman




     

  • RE: webservices uploading a 50mb file

    Raymond,

    I don't know about overhead, all I ever did was time the uploads. The "Easy" method is a bit slower (but not much, as I recall), which is why I made a threshold to not use it unless a document was over a certain size.

    If there is overhead, I imagine it would only be on the client, since that is where the file is being split out and sent in smaller chunks. Though, I suppose the server has to use a few more resources to wait for the chunks and put the file together again before saving. My knowledge of HTTP and IIS is woefully in adequate to determine whether or not there is a perceptible difference.

    Glad it worked for you, in any case, since even if it does take more resources it has to be done to get these large files uploaded!

    Thanks,
    Joe Kaufman
  • RE: webservices uploading a 50mb file

    Raymond,

    According to the documentation found here:

    https://developer.docuware.com/sdk/platform-eagle/html/57da87ed-111c-4f78-96e9-75d3b9462ce9.htm

    and

    https://developer.docuware.com/sdk/platform-eagle/html/c3431d10-b73e-4b98-814a-a0d235d21f80.htm

    there are two different upload methods you can use, and one is meant for larger files.

    Here is C# code I use to upload files, and I set the threshold at 10 MB:

     
            public static Document UploadFileToFileCabinet(FileCabinet fileCabinet, string uploadFile, Document indexInfo = null)
            {
                LastError = "";
                try
                {
                    // We will use a standard upload method for smaller files, but the "Easy" version for larger files, otherwise
                    // large file uploads might fail (the "Easy" version is meant for huge file uploads, according to the documentation).
                    int largeFileThresholdInBytes = 10 * 1024 * 1024;    // 10 MB
                    Document uploadedDoc = null;
                    FileInfo fileInfo = new FileInfo(uploadFile);
                    if (fileInfo.Length < largeFileThresholdInBytes)
                    {
                        // Smaller file.
                        uploadedDoc = fileCabinet.UploadDocument(indexInfo, fileInfo);
                    }
                    else
                    {
                        // Larger file.
                        uploadedDoc = fileCabinet.EasyUploadSingleDocument(fileInfo, indexInfo);
                    }
                    return uploadedDoc;
                }
                catch (Exception ex)
                {
                    LastError = ex.Message;
                    return null;
                }
            }

    The "Easy" method is part of some extensions, though, so I am not sure it has a direct resource end-point when doing API calls via non-NET environments.

    Are you using .NET to access the API? If not, what are you using? I see in my non-NET code (Visual FoxPro) that if I need to upload a large file I shell out to the .NET program I wrote to upload files instead of using the straight HTTP endpoint to upload. So, I must have run into the same issue you are facing. The only truly reliable way to upload huge files is with the .NET code. With that code I have uploaded files greater than 1 GB in size.

    Thanks,
    Joe Kaufman


     
  • RE: REST API - Search on Dates and Empty date field

    Frederic,

    Good info! I did not know the EMPTY() keyword could be used (or else I did know a long time ago and forgot *smile*).

    Just FYI for anyone else reading the thread, you can make the search be for a range of dates by simple adding a second "Value", like this:
     
    <?xml version="1.0" encoding="utf-8"?>
    <DialogExpression xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Operation="And" xmlns="http://dev.docuware.com/schema/public/services/platform">
        <Condition DBName="SHIP_DATE">
            <Value>2019-01-01</Value>
            <Value>2019-12-31</Value>
        </Condition>
    </DialogExpression>
    This is a query on a field called "SHIP_DATE", and would get everything where ship date was in 2019.

    For a full date/time field, such as DWSTOREDATETIME, the XML looks like:
     
    <?xml version="1.0" encoding="utf-8"?>
    <DialogExpression xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Operation="And" xmlns="http://dev.docuware.com/schema/public/services/platform">
        <Condition DBName="DWSTOREDATETIME">
            <Value>2020-01-03T00:00:00</Value>
            <Value>2020-01-03T23:59:59</Value>
        </Condition>
    </DialogExpression>

    This would get anything stored on January 3, 2020.

    Incidentally, you can use the full date/time syntax on a field that is just a "date" type and it works fine. DocuWare stores Date and DateTIme fields the same way in the database (at least in SQL Server).

    Thanks,
    Joe Kaufman



     
  • RE: Search documents based only on Docuware's document ID

    Phil,

    True enough, but not everyone can just buy a new module any time a new business need arises. I could write a console app that does this in probably a couple of hours and then set it to run every five minutes on whatever infrastructure we have developed to run automated tasks. If done right, one would use a straight SQL Server query to see where the new document ID field was zero, and then just populate that field for those documents from DWDOCID using the Platform SDK. Most IT shops have a machine (or some process) set up to run automated tasks.

    Of course, if this is a high-volume situation or a scenario where "the sooner the better" when it comes to that field being populated, there is little doubt AIX is the way to go, and probably the most cost effective in the long term. I don't know that Patricia has those needs, though.

    Now, if DocuWare would simply have AutoIndex built in, that would settle the whole issue!  *smile*

    Thanks,
    Joe Kaufman
  • RE: Search documents based only on Docuware's document ID

    Hey all,

    What about adding a DocID field to the cabinet, and then using the Platform SDK to populate it from the DWDOCID system field? This results in no custom access of the database and keeps everything in sync since the platform uses the same business logic as changing an index field from the web client.

    Patricia, is use of the Platform SDK an option? Setting this up as a console .NET application that gets set to run at regular intervals would be fairly straightforward.

    Thanks,
    Joe Kaufman
  • RE: .net upload api

    Not a problem.

    You are probably right about dialogs knowing what is hidden -- that is handy!

    Thanks,
    Joe Kaufman
  • RE: .net upload api

    Raymond,

    Just remember that dialog fields are not the same as the document (file cabinet) fields. A dialog might be configured to only show a subset of the fields, so it is only by looking at the document itself that you can know what the full structure is.

    Many layers of complexity!

    Thanks,

    Joe Kaufman

  • RE: .net upload api

    Raymond,

    As far as I know, fields/indexing is all done at the Document level, not the FileCabinet level (all of my counts are zero, too, even when iterating over the List).

    If you do something like this:
     
    DocumentsQueryResult docs = conn.GetAllDocuments(cabID);
    Document doc = docs.Items[0];
    int fieldCount = doc.Fields.Count;
    

    You will see the fields of the document, therefore from the cabinet definition.

    Thanks,
    Joe Kaufman




     
  • RE: .net upload api

    Raymond,

    I have three packages installed:

    DocuWare.RestClient
    DocuWarePlatformApi
    DocuWarePlatformApiCore

    DocwareWebclientIntegration is more for URL integration, which I implement by simply including the assembly "DocuWare.WebIntegration.dll" in my .NET project as a direct reference. The namespace you should be using in your code is "DocuWare.Platform.ServerClient". The WebClient and Platform are two separate things, and the Platform SDK stuff is all a part of "Platform".

    Thanks,
    Joe Kaufman