diff --git a/helios/election_urls.py b/helios/election_urls.py
index f63ff5845e8d0cb212a353f896b3ab27dcb6c008..4b7687623f61d1c72147217ec32ed668f2d6b4dd 100644
--- a/helios/election_urls.py
+++ b/helios/election_urls.py
@@ -68,6 +68,7 @@ urlpatterns = patterns('',
     # managing voters
     (r'^/voters/$', voter_list),
     (r'^/voters/upload$', voters_upload),
+    (r'^/voters/upload-cancel$', voters_upload_cancel),
     (r'^/voters/list$', voters_list_pretty),
     (r'^/voters/email$', voters_email),
     (r'^/voters/(?P<voter_uuid>[^/]+)$', one_voter),
diff --git a/helios/models.py b/helios/models.py
index 5763eba17f1c28380821ea6d184b7f69c5660250..5135118421f21a0bf065a18baa3d45d6bbee1685 100644
--- a/helios/models.py
+++ b/helios/models.py
@@ -448,6 +448,24 @@ class VoterFile(models.Model):
   processing_finished_at = models.DateTimeField(auto_now_add=False, null=True)
   num_voters = models.IntegerField(null=True)
 
+  def itervoters(self):
+    reader = unicode_csv_reader(open(self.voter_file.path, "rU"))
+
+    for voter_fields in reader:
+      # bad line
+      if len(voter_fields) < 1:
+        continue
+    
+      return_dict = {'voter_id': voter_fields[0]}
+
+      if len(voter_fields) > 1:
+        return_dict['email'] = voter_fields[1]
+
+      if len(voter_fields) > 2:
+        return_dict['name'] = voter_fields[2]
+
+      yield return_dict
+    
   def process(self):
     self.processing_started_at = datetime.datetime.utcnow()
     self.save()
diff --git a/helios/templates/voters_upload_confirm.html b/helios/templates/voters_upload_confirm.html
new file mode 100644
index 0000000000000000000000000000000000000000..18d5e0964270b41f7700c09781cd377fbd7a846e
--- /dev/null
+++ b/helios/templates/voters_upload_confirm.html
@@ -0,0 +1,26 @@
+{% extends TEMPLATE_BASE %}
+
+{% block content %}
+  <h2 class="title">{{election.name}} &mdash; Bulk Upload Voters &mdash; Confirm<span style="font-size:0.7em;">[<a href="{% url helios.views.one_election_view election.uuid %}">back to election</a>]</span></h2>
+
+<p>
+You have uploaded a file of voters. The first few rows of this file are:
+</p>
+
+<table>
+<tr><th>Voter Login</th><th>Email Address</th><th>Name</th></tr>
+{% for v in voters %}
+<tr><td>{{v.voter_id}}</td><td>{{v.email}}</td><td>{{v.name}}</td></tr>
+{% endfor %}
+</table>
+
+<p></p>
+<form method="post" action="" id="upload_form">
+  Does this look right to you?
+  <input type="hidden" name="confirm_p" value="1" />
+  <input type="submit" value="Yes, let's go" />
+</form>
+
+<a href="{% url helios.views.voters_upload_cancel election.uuid %}">no, let me upload a different file</a>
+
+{% endblock %}
diff --git a/helios/views.py b/helios/views.py
index c15635bbdf9526513cb233def2937649d02afb2a..da59a9ff16bd5b7c060bfb9fa375cbe5b8969011 100644
--- a/helios/views.py
+++ b/helios/views.py
@@ -941,14 +941,36 @@ def voters_upload(request, election):
     return render_template(request, 'voters_upload', {'election': election})
     
   if request.method == "POST":
-    # we store the file away for future processing
-    voters_file = request.FILES['voters_file']
-    voter_file_obj = election.add_voters_file(voters_file)
+    if bool(request.POST.get('confirm_p', 0)):
+      # launch the background task to parse that file
+      tasks.voter_file_process.delay(voter_file_id = request.session['voter_file_id'])
+      del request.session['voter_file_id']
 
-    # launch the background task to parse that file
-    tasks.voter_file_process.delay(voter_file_id = voter_file_obj.id)
+      return HttpResponseRedirect(reverse(one_election_view, args=[election.uuid]))
+    else:
+      # we need to confirm
+      voters_file = request.FILES['voters_file']
+      voter_file_obj = election.add_voters_file(voters_file)
 
-    return HttpResponseRedirect(reverse(one_election_view, args=[election.uuid]))
+      request.session['voter_file_id'] = voter_file_obj.id
+
+      # import the first few lines to check
+      voters = [v for v in voter_file_obj.itervoters()][:5]
+
+      return render_template(request, 'voters_upload_confirm', {'election': election, 'voters': voters})
+
+@election_admin()
+def voters_upload_cancel(request, election):
+  """
+  cancel upload of CSV file
+  """
+  voter_file_id = request.session.get('voter_file_id', None)
+  if voter_file_id:
+    vf = VoterFile.objects.get(id = voter_file_id)
+    vf.delete()
+  del request.session['voter_file_id']
+
+  return HttpResponseRedirect(reverse(one_election_view, args=[election.uuid]))
 
 @election_admin(frozen=True)
 def voters_email(request, election):