diff --git a/share/html/Ticket/AttachmentZip/dhandler b/share/html/Ticket/AttachmentZip/dhandler
new file mode 100644
index 0000000..4a1975b
--- /dev/null
+++ b/share/html/Ticket/AttachmentZip/dhandler
@@ -0,0 +1,67 @@
+<%init>
+# grab ticket number from URL
+my ($ticket_no) = ($m->dhandler_arg =~ m/^(\d+)\// );
+
+# create ticket object
+my $Ticket = LoadTicket($ticket_no);
+
+# collect individual attached files in a hash of arrays
+my $AttachObj = $m->comp('/Ticket/Elements/FindAttachments',
+			  Ticket => $Ticket);
+my %attached_files;
+while (my $attached_name = $AttachObj->Next() ) {
+  next unless ($attached_name->Filename());
+  push( @{ $attached_files{ $attached_name->Filename } }, $attached_name );
+}
+
+my $tempdir = File::Temp->newdir( CLEANUP => 1 );
+my $zip = Archive::Zip->new();
+my ($zip_fh,$zip_name) = Archive::Zip::tempFile($tempdir);
+
+</%init>
+<%perl>
+foreach my $name (keys %attached_files) {
+  foreach my $revision (@{$attached_files{$name}}) {
+    # temporary on-disk storage of files for zip
+    my $tempfile_fh = File::Temp->new(DIR => $tempdir, SUFFIX => ".tmp");
+
+    # these objects go out of scope before the zipfile is written to disk,
+    # which would cause them to be deleted too early.  Let the tempdir
+    # CLEANUP take care of them instead.
+    $tempfile_fh->unlink_on_destroy(0);
+    my $tempfile_name = $tempfile_fh->filename;
+    print $tempfile_fh $revision->Content;
+
+    # need to close the FH so that perl flushes output
+    close $tempfile_fh;
+
+    # add tempfile to zip as the original name from the attachment, with revision ID
+    my $member = $zip->addFile($tempfile_name,sprintf('ticket_%d/%d_%s',$Ticket->Id,$revision->Id,$revision->Filename));
+    $member->desiredCompressionMethod( COMPRESSION_DEFLATED );
+  }
+}
+
+if ( $zip->writeToFileHandle($zip_fh) == AZ_OK) {
+  # need to close the FH so that perl flushes output
+  close $zip_fh;
+  $r->content_type( "application/zip" );
+  $m->clear_buffer();
+  $m->out($m->file($zip_name));
+  $m->abort;
+} else {
+  close $zip_fh;
+  $RT::Logger->error("write error writing zip to $zip_name");
+  $r->content_type( "text/plain" );
+  $m->clear_buffer();
+  $m->out("Sorry, error creating zip.  Please contact your system administrator.");
+  $m->abort;
+}
+undef $tempdir;
+</%perl>
+<%attr>
+AutoFlush => 0
+</%attr>
+<%once>
+use Archive::Zip qw( :ERROR_CODES :CONSTANTS);
+use File::Temp qw( tempfile tempdir);
+</%once>
diff --git a/share/html/Ticket/Elements/ShowAttachments b/share/html/Ticket/Elements/ShowAttachments
index 1f476be..083f3a8 100755
--- a/share/html/Ticket/Elements/ShowAttachments
+++ b/share/html/Ticket/Elements/ShowAttachments
@@ -51,6 +51,7 @@
         class => 'ticket-info-attachments',
         color => "#336699" &>
 
+<a href="<%RT->Config->Get('WebPath')%>/Ticket/AttachmentZip/<%$Ticket->Id%>/Ticket_<%$Ticket->Id%>_attachments.zip">Download all as zip</a><br><br>
 % foreach my $key (keys %documents) {
 
 <%$key%><br />
