diff --git a/Changelog.md b/Changelog.md
index d8cebbee1add7e1fcf074d8ebce2ca3f9d8bea7a..42cdf419b91be469867befbe9149f810efcef200 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -113,6 +113,7 @@ before.
 * Redesigned the landing page and added dedicated notes for podmins [#6268](https://github.com/diaspora/diaspora/pull/6268)
 * Moved the entire federation implementation into its own gem. 🎉 [#6873](https://github.com/diaspora/diaspora/pull/6873)
 * Remove `StatusMessage#raw_message` [#6921](https://github.com/diaspora/diaspora/pull/6921)
+* Extract photo export into a service class [#6922](https://github.com/diaspora/diaspora/pull/6922)
 
 ## Bug fixes
 * Destroy Participation when removing interactions with a post [#5852](https://github.com/diaspora/diaspora/pull/5852)
diff --git a/app/models/user.rb b/app/models/user.rb
index 079ab71e76cebf6e302dd9762c1b5b737743c0ab..f6698277c7200e88a271e333987e4bf8d52d0a2f 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -324,34 +324,7 @@ class User < ActiveRecord::Base
   end
 
   def perform_export_photos!
-    temp_zip = Tempfile.new([username, '_photos.zip'])
-    begin
-      Zip::OutputStream.open(temp_zip.path) do |zos|
-        photos.each do |photo|
-          begin
-            photo_file = photo.unprocessed_image.file
-            if photo_file
-              photo_data = photo_file.read
-              zos.put_next_entry(photo.remote_photo_name)
-              zos.print(photo_data)
-            else
-              logger.info "Export photos error: No file for #{photo.remote_photo_name} not found"
-            end
-          rescue Errno::ENOENT
-            logger.info "Export photos error: #{photo.unprocessed_image.file.path} not found"
-          end
-        end
-      end
-    ensure
-      temp_zip.close
-    end
-
-    begin
-      update exported_photos_file: temp_zip, exported_photos_at: Time.zone.now if temp_zip.present?
-    ensure
-      restore_attributes if invalid? || temp_zip.present?
-      update exporting_photos: false
-    end
+    PhotoExporter.new(self).perform
   end
 
   ######### Mailer #######################
diff --git a/lib/photo_exporter.rb b/lib/photo_exporter.rb
new file mode 100644
index 0000000000000000000000000000000000000000..99c25ab98ace5dbbde1c4933a8fddbec2279ab7c
--- /dev/null
+++ b/lib/photo_exporter.rb
@@ -0,0 +1,44 @@
+class PhotoExporter
+  attr_reader :user
+
+  def initialize(user)
+    @user = user
+  end
+
+  def perform
+    temp_zip = Tempfile.new([user.username, "_photos.zip"])
+    begin
+      Zip::OutputStream.open(temp_zip.path) do |zip_output_stream|
+        user.photos.each do |photo|
+          export_photo(zip_output_stream, photo)
+        end
+      end
+    ensure
+      temp_zip.close
+    end
+
+    update_exported_photos_at(temp_zip)
+  end
+
+  private
+
+  def export_photo(zip_output_stream, photo)
+    photo_file = photo.unprocessed_image.file
+    if photo_file
+      photo_data = photo_file.read
+      zip_output_stream.put_next_entry(photo.remote_photo_name)
+      zip_output_stream.print(photo_data)
+    else
+      user.logger.info "Export photos error: No file for #{photo.remote_photo_name} not found"
+    end
+  rescue Errno::ENOENT
+    user.logger.info "Export photos error: #{photo.unprocessed_image.file.path} not found"
+  end
+
+  def update_exported_photos_at(temp_zip)
+    user.update exported_photos_file: temp_zip, exported_photos_at: Time.zone.now
+  ensure
+    user.restore_attributes if user.invalid?
+    user.update exporting_photos: false
+  end
+end