Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 610
  • Last Modified:

vb.net - move pictures

hello there,
I have a little function that tells me when the picture was taken for example "2011:02:27 17:04:45"
I have about 500 pictures in a dir "E:\My Pictures\February" all taken in different days..
what I would like to do is to make a button that will create a new "February" directory and move
the pictures in the new directories by dates.. for example:

"E:\My Pictures\February\2011\02\13"
"E:\My Pictures\February\2011\02\27"
"E:\My Pictures\February\2011\02\25"

etc.. how can I do that?
Imports System.Drawing.Imaging
Imports System.Text

Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Debug.Print(FindDateTaken("E:\My Pictures\February\PIC1001.jpg"))
    End Sub

    Function FindDateTaken(ByVal strPicture As String)
        Const DATE_TAKEN As Integer = &H9003
        Dim img As Image = Image.FromFile(strPicture)
        Dim dateTaken As String

        If img.PropertyIdList.Contains(DATE_TAKEN) Then
            Dim pitem As PropertyItem
            pitem = img.GetPropertyItem(DATE_TAKEN)
            dateTaken = Encoding.UTF8.GetString(pitem.Value, 0, pitem.Value.Length)
        Else
            dateTaken = "Not available"
        End If

        Return dateTaken
    End Function
End Class

Open in new window

0
XK8ER
Asked:
XK8ER
  • 9
  • 7
  • 4
1 Solution
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
Try something like this:  *untested
Imports System.IO
Imports System.Text
Imports System.Drawing.Imaging

Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim Pattern As String = "*.jpg"
        Dim Folder As String = "E:\My Pictures\February"

        Dim di As New DirectoryInfo(Folder)
        For Each fi As FileInfo In di.GetFiles(Pattern)
            Try
                Dim DateTaken As DateTime = FindDateTaken(fi.FullName)
                If Not IsNothing(DateTaken) Then
                    If Not Directory.Exists(Path.Combine(Folder, DateTaken.Year.ToString("00000"))) Then
                        Dim SubDi As DirectoryInfo = di.CreateSubdirectory(DateTaken.Year.ToString("0000"))
                        If Not Directory.Exists(Path.Combine(SubDi.FullName, DateTaken.Month.ToString("00"))) Then
                            SubDi = SubDi.CreateSubdirectory(DateTaken.Month.ToString("00"))
                            If Not Directory.Exists(Path.Combine(SubDi.FullName, DateTaken.Day.ToString("00"))) Then
                                SubDi.CreateSubdirectory(DateTaken.Day.ToString("00"))
                            End If
                        End If
                    End If

                    Dim NewFolder As String = Path.Combine(Folder, DateTaken.ToString("yyyy\MM\dd"))
                    Dim NewFileName As String = Path.Combine(NewFolder, fi.Name)
                    fi.MoveTo(NewFileName)
                End If
            Catch ex As Exception
                MessageBox.Show(ex.ToString, "Error Processing Image", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
            End Try
        Next
    End Sub

    Private Function FindDateTaken(ByVal strPicture As String) As DateTime
        Const DATE_TAKEN As Integer = &H9003
        Dim img As Image = Image.FromFile(strPicture)
        Dim dateTaken As String

        If img.PropertyIdList.Contains(DATE_TAKEN) Then
            Dim pitem As PropertyItem
            pitem = img.GetPropertyItem(DATE_TAKEN)
            dateTaken = Encoding.UTF8.GetString(pitem.Value, 0, pitem.Value.Length)
            Dim dt As DateTime
            If DateTime.TryParseExact(dateTaken, "yyyy:MM:dd HH:mm:ss", Nothing, Globalization.DateTimeStyles.None, dt) Then
                Return dt
            End If
        End If

        Return Nothing
    End Function

End Class

Open in new window

0
 
XK8ERAuthor Commented:
it says the process its being used
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
Try this FindDateTaken() function instead:
Private Function FindDateTaken(ByVal strPicture As String) As DateTime
        Const DATE_TAKEN As Integer = &H9003
        Dim img As Image
        Using fs As New FileStream(strPicture, FileMode.Open)
            img = Image.FromStream(fs)
        End Using

        Dim dateTaken As String
        If img.PropertyIdList.Contains(DATE_TAKEN) Then
            Dim pitem As PropertyItem
            pitem = img.GetPropertyItem(DATE_TAKEN)
            dateTaken = Encoding.UTF8.GetString(pitem.Value, 0, pitem.Value.Length)
            Dim dt As DateTime
            If DateTime.TryParseExact(dateTaken, "yyyy:MM:dd HH:mm:ss", Nothing, Globalization.DateTimeStyles.None, dt) Then
                Return dt
            End If
        End If

        Return Nothing
    End Function

Open in new window

0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
XK8ERAuthor Commented:
still file in use.. and says line 28 which is this >>fi.MoveTo(NewFileName)
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
Hmmm...well, the act of reading the EXIF data from the file will place a temporary lock on it.  The operating system itself will take an indeterminate amount of time to release the file even though from a .Net perspective we are done with it.

Possible solutions:
(1) Trap the exception and loop until the operation is successful.  *Possibly with a max number of attempts?
(2) Add the files to be moved (along with the new target filename) to a List(Of String) that is processed periodically from a Timer.  As the files are moved you remove them from the List.  If an exception occurs you simply leave it there.  Turn off the Timer when the List is empty.
0
 
XK8ERAuthor Commented:
well I commented that line for testing purposes and its not creating the right directory structure..

all I see is "E:\My Pictures\February\0001\01\01" which is wrong..

0
 
XK8ERAuthor Commented:
maybe the issue is in FindDateTaken function cus its returning a time and not a date..
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
Check what is being extracted in FindDateTaken():

            Dim dt As DateTime
            If DateTime.TryParseExact(dateTaken, "yyyy:MM:dd HH:mm:ss", Nothing, Globalization.DateTimeStyles.None, dt) Then
                Debug.Print(strPicture & " | " & dt.ToString)
                Return dt
            End If
0
 
MedievalWarriorCommented:
Hi,

You have to close the image stream before moving the file.
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
In the second version of FindDateTaken() it should be closed automatically as a "Using" block was implemented:

        Using fs As New FileStream(strPicture, FileMode.Open)
            img = Image.FromStream(fs)
        End Using
0
 
MedievalWarriorCommented:
Your right Idle_Mind I didn't see that part. I was looking at the authors original code. I think part of the problem could be blank (date taken) properties, images in which no information exists returns null datetime value.
Debug.Print(New DateTime(Nothing))

Open in new window

0
 
MedievalWarriorCommented:
Here is your test case.
Debug.Print(Test)
Debug.Print(IsNothing(Test))

Private Function Test() As DateTime
 Return Nothing
End Function

Open in new window

0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
Good spot Medieval.  We could return DateTime.MinValue (or MaxValue) instead of Nothing and check for that instead.
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
Note that I had "*untested" on my original post...I don't do that very often.  =\
0
 
MedievalWarriorCommented:
I have have done much worst things than that Idle_Mind. In fact I have already run into this behavior before and I always use IsNot Nothing because this will let you know if the object in question is a reference type.
If object IsNot Nothing Then

Open in new window

0
 
XK8ERAuthor Commented:
>>Debug.Print(strPicture & " | " & dt.ToString)

doesn't output anything
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
Move it up one line and see what "dateTaken" is:

            dateTaken = Encoding.UTF8.GetString(pitem.Value, 0, pitem.Value.Length)
            Debug.Print("dateTaken = " & dateTaken & " | strPicture = " & strPicture)
0
 
XK8ERAuthor Commented:
it doesn't show anything.. the original code that I posted it does show the date..
0
 
Mike TomlinsonMiddle School Assistant TeacherCommented:
Apologies for the bugs in the code.  For some reason I couldn't get the Parse() method to work even though I had valid values in there.  Also, the Image.FromStream() did not work the same as Image.FromFile()....bizarre!

Here is a *TESTED* version:
Imports System.IO
Imports System.Text
Imports System.Drawing.Imaging
Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        If FolderBrowserDialog1.ShowDialog = Windows.Forms.DialogResult.OK Then
            ProcessFiles(FolderBrowserDialog1.SelectedPath, "*.jpg")
        End If
    End Sub

    Private Sub ProcessFiles(ByVal Folder As String, ByVal Pattern As String)
        Dim di As New DirectoryInfo(Folder)
        For Each fi As FileInfo In di.GetFiles(Pattern)
            Dim values() As String = FindDateTaken(fi.FullName).Split(":")
            If values.Length >= 2 Then
                values(2) = values(2).Split(" ")(0)
                Try
                    Dim dt As New DateTime(values(0), values(1), values(2))
                    Dim SubDi As New DirectoryInfo(Path.Combine(di.FullName, dt.Year))
                    If Not SubDi.Exists() Then
                        SubDi.Create()
                    End If
                    SubDi = New DirectoryInfo(Path.Combine(SubDi.FullName, dt.Month.ToString("00")))
                    If Not SubDi.Exists() Then
                        SubDi.Create()
                    End If
                    SubDi = New DirectoryInfo(Path.Combine(SubDi.FullName, dt.Day.ToString("00")))
                    If Not SubDi.Exists() Then
                        SubDi.Create()
                    End If

                    Dim NewFileName As String = Path.Combine(SubDi.FullName, fi.Name)
                    fi.MoveTo(NewFileName)
                Catch ex As Exception

                End Try
            End If
        Next
    End Sub

    Private Function FindDateTaken(ByVal strPicture As String) As String
        Const DATE_TAKEN As Integer = &H9003
        Dim dateTaken As String = ""
        Using img As Image = Image.FromFile(strPicture)
            If img.PropertyIdList.Contains(DATE_TAKEN) Then
                Dim pitem As PropertyItem = img.GetPropertyItem(DATE_TAKEN)
                dateTaken = Encoding.UTF8.GetString(pitem.Value, 0, pitem.Value.Length)
            End If
        End Using
        Return dateTaken
    End Function

End Class

Open in new window

0
 
XK8ERAuthor Commented:
perfection.. thank you so much!
0

Featured Post

Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

  • 9
  • 7
  • 4
Tackle projects and never again get stuck behind a technical roadblock.
Join Now