diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb
index 5bd0c8cd78078ac63f88930a14d96382cc93e2d4..ee1b1f375dc2651cd2099767bbc4e3d7e8269544 100644
--- a/app/controllers/projects/artifacts_controller.rb
+++ b/app/controllers/projects/artifacts_controller.rb
@@ -16,10 +16,7 @@ class Projects::ArtifactsController < Projects::ApplicationController
 
   def browse
     return render_404 unless build.artifacts?
-
-    current_path = params[:path] ? "./#{params[:path]}/" : './'
-    paths, metadata = build.artifacts_metadata_for_path(current_path)
-    @path = Gitlab::StringPath.new(current_path, paths, metadata)
+    @path = build.artifacts_metadata_string_path(params[:path] || './')
   end
 
   private
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index df51a5ce079d7ef97d5955cdd8511c4f55d5cae0..7983ce0e88ec1b2156beaf1633f89d7e57a2027b 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -349,24 +349,10 @@ module Ci
       artifacts? && artifacts_file.path.end_with?('zip') && artifacts_metadata.exists?
     end
 
-    def artifacts_metadata_for_path(path)
-      return [] unless artifacts_metadata.exists?
-      paths, metadata = [], []
-
-      metadata_path = path.sub(/^\.\//, '')
-      File.open(artifacts_metadata.path) do |file|
-        gzip = Zlib::GzipReader.new(file)
-        gzip.each_line do |line|
-          if line =~ %r{^#{Regexp.escape(metadata_path)}[^/\s]+/?\s}
-            matched_path, matched_meta =  line.split(' ')
-            paths << matched_path
-            metadata << JSON.parse(matched_meta)
-          end
-        end
-        gzip.close
-      end
 
-      [paths, metadata]
+    def artifacts_metadata_string_path(path)
+      file = artifacts_metadata.path
+      Gitlab::Ci::Build::Artifacts::Metadata.new(file, path).to_string_path
     end
 
     private
diff --git a/lib/gitlab/ci/build/artifacts/metadata.rb b/lib/gitlab/ci/build/artifacts/metadata.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5313182d55f4a2fe7e5f65dfabc9c545bf607f77
--- /dev/null
+++ b/lib/gitlab/ci/build/artifacts/metadata.rb
@@ -0,0 +1,57 @@
+require 'zlib'
+require 'json'
+
+module Gitlab
+  module Ci
+    module Build
+      module Artifacts
+        class Metadata
+          def initialize(file, path)
+            @file = file
+
+            @path = path.sub(/^\.\//, '')
+            @path << '/' unless path.end_with?('/')
+          end
+
+          def exists?
+            File.exists?(@file)
+          end
+
+          def match!
+            raise StandardError, 'Metadata file not found !' unless exists?
+            paths, metadata = [], []
+
+            each do |line|
+              next unless line =~ %r{^#{Regexp.escape(@path)}[^/\s]+/?\s}
+
+              path, meta = line.split(' ')
+              paths.push(path)
+              metadata.push(meta)
+           end
+
+            [paths, metadata.map { |meta| JSON.parse(meta) }]
+          end
+
+          def to_string_path
+            universe, metadata = match!
+            ::Gitlab::StringPath.new(@path, universe, metadata)
+          end
+
+          private
+
+          def each
+            open do |file|
+              gzip = Zlib::GzipReader.new(file)
+              gzip.each_line { |line| yield line }
+              gzip.close
+            end
+          end
+
+          def open
+            File.open(@file) { |file| yield file }
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/string_path.rb b/lib/gitlab/string_path.rb
index e7d99a35869e86cc431023821afcc55beb56b842..9948502e8ea343c425d44bdf413a7e426da8f027 100644
--- a/lib/gitlab/string_path.rb
+++ b/lib/gitlab/string_path.rb
@@ -75,7 +75,7 @@ module Gitlab
     end
 
     def directories!
-      has_parent? ? directories.prepend(parent) : directories
+      @path =~ %r{^\./[^/]/} ? directories.prepend(parent) : directories
     end
 
     def files
@@ -119,7 +119,7 @@ module Gitlab
       raise ArgumentError, 'Invalid path' if clean_path.start_with?('../')
 
       prefix = './' unless clean_path =~ %r{^[\.|/]}
-      suffix = '/' if path.end_with?('/') || clean_path =~ /^[\.|\.\.]$/
+      suffix = '/' if path.end_with?('/') || ['.', '..'].include?(clean_path)
       prefix.to_s + clean_path + suffix.to_s
     end
   end
diff --git a/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb b/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8c648be5f028d3662c46f53f20d5646fd6e047db
--- /dev/null
+++ b/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
@@ -0,0 +1,76 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Build::Artifacts::Metadata do
+  def metadata(path = '')
+    described_class.new(metadata_file_path, path)
+  end
+
+  let(:metadata_file_path) do
+    Rails.root + 'spec/fixtures/ci_build_artifacts_metadata.gz'
+  end
+
+  context 'metadata file exists' do
+    describe '#exists?' do
+      subject { metadata.exists? }
+      it { is_expected.to be true }
+    end
+
+    describe '#match! ./' do
+      subject { metadata('./').match! }
+
+      it 'matches correct paths' do
+        expect(subject.first).to contain_exactly 'ci_artifacts.txt',
+                                                 'other_artifacts_0.1.2/',
+                                                 'rails_sample.jpg'
+      end
+
+      it 'matches metadata for every path' do
+        expect(subject.last.count).to eq 3
+      end
+
+      it 'return Hashes for each metadata' do
+        expect(subject.last).to all(be_kind_of(Hash))
+      end
+    end
+
+    describe '#match! other_artifacts_0.1.2' do
+      subject { metadata('other_artifacts_0.1.2').match! }
+
+      it 'matches correct paths' do
+        expect(subject.first).
+          to contain_exactly 'other_artifacts_0.1.2/doc_sample.txt',
+                             'other_artifacts_0.1.2/another-subdirectory/'
+      end
+    end
+
+    describe '#match! other_artifacts_0.1.2/another-subdirectory' do
+      subject { metadata('other_artifacts_0.1.2/another-subdirectory/').match! }
+
+      it 'matches correct paths' do
+        expect(subject.first).
+          to contain_exactly 'other_artifacts_0.1.2/another-subdirectory/empty_directory/',
+                             'other_artifacts_0.1.2/another-subdirectory/banana_sample.gif'
+      end
+    end
+
+    describe '#to_string_path' do
+      subject { metadata('').to_string_path }
+      it { is_expected.to be_an_instance_of(Gitlab::StringPath) }
+    end
+  end
+
+  context 'metadata file does not exist' do
+    let(:metadata_file_path) { '' }
+
+    describe '#exists?' do
+      subject { metadata.exists? }
+      it { is_expected.to be false }
+    end
+
+    describe '#match!' do
+      it 'raises error' do
+        expect { metadata.match! }.to raise_error(StandardError, /Metadata file not found/)
+      end
+    end
+  end
+end