Upload or ZIP is locking file

2 posts, 1 answers
  1. Dejan
    Dejan avatar
    5 posts
    Member since:
    Oct 2011

    Posted 06 Oct 2011 Link to this post

    I’m trying to fulfill requirements:

     

    1. Select a ZIP file on Silverlight side
    2. Upload it to server
    3. Unpack it
    4. Search over extracted file and find manifest.xml
    5. Collect information from manifest
    6. Zip file itself it not required anymore and can be deleted from server file system.
    7. Return information from manifest to Silverlight calling page

     

    For that purpose I implemented following handler:

     

    /// <summary>
        /// Handler for managing files uploaded by LearningObjectUploadWindow.xaml
        /// </summary>
        public class RadUploadLearningObjectHandler : Telerik.Windows.RadUploadHandler
        {
            /// <summary>
            /// this dictionary will be used to transfer data extracted from zip file to calling silverlight control
            /// </summary>
            private Dictionary<string, object> _dict;
      
            /// <summary>
            /// files are sent in chunks of data
            /// </summary>
            /// <param name="filePath"></param>
            /// <param name="position"></param>
            /// <param name="buffer"></param>
            /// <param name="contentLength"></param>
            /// <param name="savedBytes"></param>
            /// <returns></returns>
            public override bool SaveChunkData(string filePath, long position, byte[] buffer, int contentLength, out int savedBytes)
            {
                if (this.IsNewFileRequest())
                {
                    PrepareStorageFolder();
                }
      
                bool result = base.SaveChunkData(filePath, position, buffer, contentLength, out savedBytes);
      
      
                // Checks if this is the last chunk of each file
                if (this.IsFinalFileRequest())
                {
                    // Get the names of all the files that have been uploaded so far
                    string uploadedFiles = this.GetQueryParameter("UploadedFiles");
      
                    //if the file is successfully uploaded
                    if (result)
                    {
                        // attach the name of the last file
                        uploadedFiles += this.GetFilePath();
      
                        // format the string
                        uploadedFiles = uploadedFiles.Replace(@"\\", @"\");
      
                        // create a collection of filenames
                        string[] fileNames = uploadedFiles.Split('\n');
      
                        ProcessUploadedFiles(fileNames);
                    }
                }
      
                return result;
            }
      
      
            /// <summary>
            /// after files are uploaded and before additional informations are transfered to calling silverlight control 
            /// </summary>
            /// <param name="fileNames"></param>
            private void ProcessUploadedFiles(string[] fileNames)
            {
                _dict = base.GetAssociatedData();
      
                foreach (var filename in fileNames)
                {
                    if (filename.EndsWith(".zip"))
                    {
                        _dict.Add("FileType", "ZIP");
      
                        string folderPath = this.GetTargetFolder();
      
                        //extract all files from archieve
                        using (var package = ZipPackage.OpenFile(filename, FileAccess.Read))
                        {
                            foreach (var entry in package.ZipPackageEntries)
                            {
                                if (entry.Attributes != FileAttributes.Directory)
                                {
                                    UnpackAnSaveFile(entry, folderPath);
                                }
                            }
                        }
      
      
                        //PROBLEM!
                        //File is locked by exctractor (or by handler) for a while so it can not be deleted at this moment
                        //todo dkos 20111006 find a way to exctract and delete zip file using another aproach
                        File.Delete(filename);
      
                        //read manifest file
                        var manifestFileName = folderPath + "\\manifest.xml";
      
                        if (File.Exists(manifestFileName))
                        {
                            ExtractDataFromManifest(manifestFileName);
                        }
      
                    }
                    else
                    {
                        _dict.Add(Constants.LEARNING_OBJECT_START_PATH_KEY, this.GetFileName());
                        _dict.Add(Constants.LEARNING_OBJECT_TYPE_KEY, "DOCU");
                    }
                }
            }
      
            /// <summary>
            /// return all data added to dictionary in ProcessUploadedFiles()
            /// </summary>
            /// <returns></returns>
            public override Dictionary<string, object> GetAssociatedData()
            {
                return _dict;
            }
      
            /// <summary>
            /// reading info from manifest file
            /// </summary>
            /// <param name="manifestPath"></param>
            private void ExtractDataFromManifest(string manifestPath)
            {
      
                try
                {
                    // try to load the manifest.xml file
                    XmlDocument doc = new XmlDocument();
                    doc.Load(manifestPath);
                    XmlElement root = (XmlElement)doc.GetElementsByTagName("manifest")[0];
      
                    // get obligate startUrl
                    var startUrl = root.GetElementsByTagName(Constants.LEARNING_OBJECT_START_PATH_KEY)[0].InnerText;
      
                    _dict.Add(Constants.LEARNING_OBJECT_START_PATH_KEY, startUrl);
      
      
                    // set the optional defaults:
      
                    // ..short name
                    XmlNodeList nl = root.GetElementsByTagName(Constants.LEARNING_OBJECT_SHORT_NAME_KEY);
                    if (nl.Count == 1)
                        if (nl[0].InnerText.Length > 10)
                            _dict.Add(Constants.LEARNING_OBJECT_SHORT_NAME_KEY, nl[0].InnerText.Substring(0, 10));
                        else
                            _dict.Add(Constants.LEARNING_OBJECT_SHORT_NAME_KEY, nl[0].InnerText);
      
                    // ..name
                    nl = root.GetElementsByTagName(Constants.LEARNING_OBJECT_NAME_KEY);
                    if (nl.Count == 1)
                        _dict.Add(Constants.LEARNING_OBJECT_NAME_KEY, nl[0].InnerText);
      
                    // ..description
                    nl = root.GetElementsByTagName(Constants.LEARNING_OBJECT_DESCRIPTION_KEY);
                    if (nl.Count == 1)
                        _dict.Add(Constants.LEARNING_OBJECT_DESCRIPTION_KEY, nl[0].InnerText);
      
                    // ..languageId
                    nl = root.GetElementsByTagName(Constants.LEARNING_OBJECT_LANGUAGE_KEY);
                    if (nl.Count == 1)
                        _dict.Add(Constants.LEARNING_OBJECT_LANGUAGE_KEY, nl[0].InnerText);
      
                    // ..keywords
                    nl = root.GetElementsByTagName(Constants.LEARNING_OBJECT_KEYWORDS_KEY);
                    if (nl.Count == 1)
                        _dict.Add(Constants.LEARNING_OBJECT_KEYWORDS_KEY, nl[0].InnerText);
      
                    // ..type
                    nl = root.GetElementsByTagName(Constants.LEARNING_OBJECT_TYPE_KEY);
                    if (nl.Count == 1)
                        _dict.Add(Constants.LEARNING_OBJECT_TYPE_KEY, nl[0].InnerText.ToUpper());
      
                    // ..workTimeMinutes
                    nl = root.GetElementsByTagName(Constants.LEARNING_OBJECT_WORK_TIME_MINUTES_KEY);
                    if (nl.Count == 1)
                        try
                        {
                            _dict.Add(Constants.LEARNING_OBJECT_WORK_TIME_MINUTES_KEY, int.Parse(nl[0].InnerText));
                        }
                        catch (Exception exc)
                        {
                            throw new Exception("workTimeMinutes property is not integer", exc);
                        }
      
                    // ..contentAuthor
                    nl = root.GetElementsByTagName(Constants.LEARNING_OBJECT_CONTENT_AUTHOR_KEY);
                    if (nl.Count == 1)
                        _dict.Add(Constants.LEARNING_OBJECT_CONTENT_AUTHOR_KEY, nl[0].InnerText);
      
      
                }
                catch (Exception ex)
                {
                    throw new Exception("Error in reading manifest file", ex);
                }
      
      
      
            }
      
            /// <summary>
            /// exctracting each file from zip archive
            /// </summary>
            /// <param name="entry"></param>
            /// <param name="folderPath"></param>
            private static void UnpackAnSaveFile(ZipPackageEntry entry, string folderPath)
            {
                Stream reader = entry.OpenInputStream();
                var fileName = folderPath + "\\" + entry.FileNameInZip;
      
                var directoryName = Path.GetDirectoryName(fileName);
      
                if (directoryName != null)
                {
                    if (!Directory.Exists(directoryName))
                    {
                        Directory.CreateDirectory(directoryName);
                    }
      
                    FileStream writer = File.Create(folderPath + "\\" + entry.FileNameInZip);
      
                    int size = 2048;
                    byte[] data = new byte[2048];
                    while (true)
                    {
                        size = reader.Read(data, 0, data.Length);
                        if (size > 0)
                            writer.Write(data, 0, size);
                        else
                            break;
                    }
                    writer.Close();
                }
      
            }
      
      
            internal void PrepareStorageFolder()
            {
                string folderPath = this.GetTargetFolder();
                if (Directory.Exists(folderPath))
                {
      
                    //todo dkos 20111006 choose: do we want to delete old content or to move it into archive
                    //note: both proces will fail if modification is immediately after old content is uploaded (problem with locking ZIP archive - lines: 85-96) 
      
      
                    //move to archive
                    var dest = folderPath + "_" + DateTime.Now.ToString("yyyyMMdd_hhmmss");
                    Directory.Move(folderPath, dest);
      
                    //delete
                    //Directory.Delete(folderPath,true);
                }
      
                Directory.CreateDirectory(folderPath);
                if (!Directory.Exists(folderPath))
                {
                    throw new DirectoryNotFoundException(folderPath);
                }
            }
        }

     

     

     

    Problem starts when I want to delete ZIP file (line 100: //File.Delete(filename);)

     

    I can live with the fact that ZIP file remains on the server but there is another scenario:

    If user wants to modify Learning Object by uploading new archive, entire content in (existing) folder has to be deleted first.
    But piece of code:

     

    internal void PrepareStorageFolder()
            {
                string folderPath = this.GetTargetFolder();
                if (Directory.Exists(folderPath))
                {
      
                    //todo dkos 20111006 choose: do we want to delete old content or to move it into archive
                    //note: both proces will fail if modification is immediately after old content is uploaded (problem with locking ZIP archive - lines: 85-96) 
      
      
                    //move to archive
                    var dest = folderPath + "_" + DateTime.Now.ToString("yyyyMMdd_hhmmss");
                    Directory.Move(folderPath, dest);
      
                    //delete
                    //Directory.Delete(folderPath,true);
                }
      
                Directory.CreateDirectory(folderPath);
                if (!Directory.Exists(folderPath))
                {
                    throw new DirectoryNotFoundException(folderPath);
                }
            }

     

     

    Fails with same error if I try to modify Learning Object immediately after uploading it:

     

    {"The process cannot access the file 'D:\\Projects\\KV-Reform\\Organizer\\production\\src\\T2LOrganizer.Web\\app\\learn_catalog\\content\\e2b00a83-df4b-4c10-b2c8-0269a7a0e7b9\\ST_711_08d.zip' because it is being used by another process."}

     

       at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)

       at System.IO.File.Delete(String path)

       at T2LOrganizer.Web.AspHandlers.RadUploadLearningObjectHandler.ProcessUploadedFiles(String[] fileNames) in D:\Projects\KV-Reform\Organizer\production\src\T2LOrganizer.Web\AspHandlers\RadUploadLearningObjectHandler.ashx.cs:line 100

       at T2LOrganizer.Web.AspHandlers.RadUploadLearningObjectHandler.SaveChunkData(String filePath, Int64 position, Byte[] buffer, Int32 contentLength, Int32& savedBytes) in D:\Projects\KV-Reform\Organizer\production\src\T2LOrganizer.Web\AspHandlers\RadUploadLearningObjectHandler.ashx.cs:line 60

       at Telerik.Windows.RadUploadHandler.ProcessStream()

     

    If I try later (e.g. 10 minutes after) everything is fine.

    Something is blocking uploaded file for a while: handler it selves or ZipPackage.

     

    Any idea how to solve this?

    Thanks in advance.

     

    Dekos

     

  2. Answer
    Tina Stancheva
    Admin
    Tina Stancheva avatar
    3298 posts

    Posted 11 Oct 2011 Link to this post

    Hello Dejan,

    In order to get over this issue you can change the method used to open the ZipPackage. Instead of using the OpenFile() method, you can use the Open() method:
    foreach (var filename in fileNames)
    {
        if (filename.EndsWith(".zip"))
        {
            _dict.Add("FileType", "ZIP");
            FileStream fileStream = new FileStream(filename, FileMode.Open);
            string folderPath = this.GetTargetFolder();
     
            //extract all files from archieve
            using (var package = ZipPackage.Open(fileStream, FileAccess.Read))
            {
                foreach (var entry in package.ZipPackageEntries)
                {
                    if (entry.Attributes != FileAttributes.Directory)
                    {
                        UnpackAnSaveFile(entry, folderPath);
                    }
                }
            }
            ...
        }
        ...
    }

    Give this a try and let us know if you still experience any issues. In the meantime we will investigate further the file access issue you reported. And as a small sign of appreciation fro bringing it to our attention, I updated your Telerik account.

    Best wishes,
    Tina Stancheva
    the Telerik team

    Explore the entire Telerik portfolio by downloading the Ultimate Collection trial package. Get it now >>

  3. DevCraft banner
Back to Top