Mercurial > hg > orthanc
comparison OrthancServer/OrthancRestApi/OrthancRestArchive.cpp @ 1778:776573e592da
create media refactoring
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 13 Nov 2015 13:12:21 +0100 |
parents | 0f5c416969dc |
children | 559956d5ceb2 |
comparison
equal
deleted
inserted
replaced
1777:0f5c416969dc | 1778:776573e592da |
---|---|
52 | 52 |
53 namespace Orthanc | 53 namespace Orthanc |
54 { | 54 { |
55 // Download of ZIP files ---------------------------------------------------- | 55 // Download of ZIP files ---------------------------------------------------- |
56 | 56 |
57 static std::string GetDirectoryNameInArchive(const Json::Value& resource, | |
58 ResourceType resourceType) | |
59 { | |
60 std::string s; | |
61 const Json::Value& tags = resource["MainDicomTags"]; | |
62 | |
63 switch (resourceType) | |
64 { | |
65 case ResourceType_Patient: | |
66 { | |
67 std::string p = tags["PatientID"].asString(); | |
68 std::string n = tags["PatientName"].asString(); | |
69 s = p + " " + n; | |
70 break; | |
71 } | |
72 | |
73 case ResourceType_Study: | |
74 { | |
75 std::string p; | |
76 if (tags.isMember("AccessionNumber")) | |
77 { | |
78 p = tags["AccessionNumber"].asString() + " "; | |
79 } | |
80 | |
81 s = p + tags["StudyDescription"].asString(); | |
82 break; | |
83 } | |
84 | |
85 case ResourceType_Series: | |
86 { | |
87 std::string d = tags["SeriesDescription"].asString(); | |
88 std::string m = tags["Modality"].asString(); | |
89 s = m + " " + d; | |
90 break; | |
91 } | |
92 | |
93 default: | |
94 throw OrthancException(ErrorCode_InternalError); | |
95 } | |
96 | |
97 // Get rid of special characters | |
98 return Toolbox::ConvertToAscii(s); | |
99 } | |
100 | |
101 static bool CreateRootDirectoryInArchive(HierarchicalZipWriter& writer, | |
102 ServerContext& context, | |
103 const Json::Value& resource, | |
104 ResourceType resourceType) | |
105 { | |
106 if (resourceType == ResourceType_Patient) | |
107 { | |
108 return true; | |
109 } | |
110 | |
111 ResourceType parentType = GetParentResourceType(resourceType); | |
112 Json::Value parent; | |
113 | |
114 switch (resourceType) | |
115 { | |
116 case ResourceType_Study: | |
117 { | |
118 if (!context.GetIndex().LookupResource(parent, resource["ParentPatient"].asString(), parentType)) | |
119 { | |
120 return false; | |
121 } | |
122 | |
123 break; | |
124 } | |
125 | |
126 case ResourceType_Series: | |
127 if (!context.GetIndex().LookupResource(parent, resource["ParentStudy"].asString(), parentType) || | |
128 !CreateRootDirectoryInArchive(writer, context, parent, parentType)) | |
129 { | |
130 return false; | |
131 } | |
132 break; | |
133 | |
134 default: | |
135 throw OrthancException(ErrorCode_NotImplemented); | |
136 } | |
137 | |
138 writer.OpenDirectory(GetDirectoryNameInArchive(parent, parentType).c_str()); | |
139 return true; | |
140 } | |
141 | |
142 static bool ArchiveInstance(HierarchicalZipWriter& writer, | |
143 ServerContext& context, | |
144 const std::string& instancePublicId, | |
145 const char* filename) | |
146 { | |
147 writer.OpenFile(filename); | |
148 | |
149 std::string dicom; | |
150 context.ReadFile(dicom, instancePublicId, FileContentType_Dicom); | |
151 writer.Write(dicom); | |
152 | |
153 return true; | |
154 } | |
155 | |
156 static bool ArchiveInternal(HierarchicalZipWriter& writer, | |
157 ServerContext& context, | |
158 const std::string& publicId, | |
159 ResourceType resourceType, | |
160 bool isFirstLevel) | |
161 { | |
162 Json::Value resource; | |
163 if (!context.GetIndex().LookupResource(resource, publicId, resourceType)) | |
164 { | |
165 return false; | |
166 } | |
167 | |
168 if (isFirstLevel && | |
169 !CreateRootDirectoryInArchive(writer, context, resource, resourceType)) | |
170 { | |
171 return false; | |
172 } | |
173 | |
174 writer.OpenDirectory(GetDirectoryNameInArchive(resource, resourceType).c_str()); | |
175 | |
176 switch (resourceType) | |
177 { | |
178 case ResourceType_Patient: | |
179 for (Json::Value::ArrayIndex i = 0; i < resource["Studies"].size(); i++) | |
180 { | |
181 std::string studyId = resource["Studies"][i].asString(); | |
182 if (!ArchiveInternal(writer, context, studyId, ResourceType_Study, false)) | |
183 { | |
184 return false; | |
185 } | |
186 } | |
187 break; | |
188 | |
189 case ResourceType_Study: | |
190 for (Json::Value::ArrayIndex i = 0; i < resource["Series"].size(); i++) | |
191 { | |
192 std::string seriesId = resource["Series"][i].asString(); | |
193 if (!ArchiveInternal(writer, context, seriesId, ResourceType_Series, false)) | |
194 { | |
195 return false; | |
196 } | |
197 } | |
198 break; | |
199 | |
200 case ResourceType_Series: | |
201 { | |
202 // Create a filename prefix, depending on the modality | |
203 char format[24] = "%08d.dcm"; | |
204 | |
205 if (resource["MainDicomTags"].isMember("Modality")) | |
206 { | |
207 std::string modality = resource["MainDicomTags"]["Modality"].asString(); | |
208 | |
209 if (modality.size() == 1) | |
210 { | |
211 snprintf(format, sizeof(format) - 1, "%c%%07d.dcm", toupper(modality[0])); | |
212 } | |
213 else if (modality.size() >= 2) | |
214 { | |
215 snprintf(format, sizeof(format) - 1, "%c%c%%06d.dcm", toupper(modality[0]), toupper(modality[1])); | |
216 } | |
217 } | |
218 | |
219 char filename[24]; | |
220 | |
221 for (Json::Value::ArrayIndex i = 0; i < resource["Instances"].size(); i++) | |
222 { | |
223 snprintf(filename, sizeof(filename) - 1, format, i); | |
224 | |
225 std::string publicId = resource["Instances"][i].asString(); | |
226 | |
227 // This was the implementation up to Orthanc 0.7.0: | |
228 // std::string filename = instance["MainDicomTags"]["SOPInstanceUID"].asString() + ".dcm"; | |
229 | |
230 if (!ArchiveInstance(writer, context, publicId, filename)) | |
231 { | |
232 return false; | |
233 } | |
234 } | |
235 | |
236 break; | |
237 } | |
238 | |
239 default: | |
240 throw OrthancException(ErrorCode_InternalError); | |
241 } | |
242 | |
243 writer.CloseDirectory(); | |
244 return true; | |
245 } | |
246 | |
247 | |
248 static bool IsZip64Required(uint64_t uncompressedSize, | 57 static bool IsZip64Required(uint64_t uncompressedSize, |
249 unsigned int countInstances) | 58 unsigned int countInstances) |
250 { | 59 { |
251 static const uint64_t SAFETY_MARGIN = 64 * MEGA_BYTES; | 60 static const uint64_t SAFETY_MARGIN = 64 * MEGA_BYTES; |
252 | 61 |
264 << (uncompressedSize / MEGA_BYTES) << "MB using the " | 73 << (uncompressedSize / MEGA_BYTES) << "MB using the " |
265 << (isZip64 ? "ZIP64" : "ZIP32") << " file format"; | 74 << (isZip64 ? "ZIP64" : "ZIP32") << " file format"; |
266 | 75 |
267 return isZip64; | 76 return isZip64; |
268 } | 77 } |
269 | |
270 | |
271 static bool IsZip64Required(ServerIndex& index, | |
272 const std::string& id) | |
273 { | |
274 uint64_t uncompressedSize; | |
275 uint64_t compressedSize; | |
276 unsigned int countStudies; | |
277 unsigned int countSeries; | |
278 unsigned int countInstances; | |
279 | |
280 index.GetStatistics(compressedSize, uncompressedSize, | |
281 countStudies, countSeries, countInstances, id); | |
282 | |
283 return IsZip64Required(uncompressedSize, countInstances); | |
284 } | |
285 | |
286 | |
287 template <enum ResourceType resourceType> | |
288 static void GetArchive(RestApiGetCall& call) | |
289 { | |
290 ServerContext& context = OrthancRestApi::GetContext(call); | |
291 | |
292 std::string id = call.GetUriComponent("id", ""); | |
293 bool isZip64 = IsZip64Required(context.GetIndex(), id); | |
294 | |
295 // Create a RAII for the temporary file to manage the ZIP file | |
296 Toolbox::TemporaryFile tmp; | |
297 | |
298 { | |
299 // Create a ZIP writer | |
300 HierarchicalZipWriter writer(tmp.GetPath().c_str()); | |
301 writer.SetZip64(isZip64); | |
302 | |
303 // Store the requested resource into the ZIP | |
304 if (!ArchiveInternal(writer, context, id, resourceType, true)) | |
305 { | |
306 return; | |
307 } | |
308 } | |
309 | |
310 // Prepare the sending of the ZIP file | |
311 FilesystemHttpSender sender(tmp.GetPath()); | |
312 sender.SetContentType("application/zip"); | |
313 sender.SetContentFilename(id + ".zip"); | |
314 | |
315 // Send the ZIP | |
316 call.GetOutput().AnswerStream(sender); | |
317 | |
318 // The temporary file is automatically removed thanks to the RAII | |
319 } | |
320 | |
321 | |
322 static void GetMediaArchive(RestApiGetCall& call) | |
323 { | |
324 ServerContext& context = OrthancRestApi::GetContext(call); | |
325 | |
326 std::string id = call.GetUriComponent("id", ""); | |
327 bool isZip64 = IsZip64Required(context.GetIndex(), id); | |
328 | |
329 // Create a RAII for the temporary file to manage the ZIP file | |
330 Toolbox::TemporaryFile tmp; | |
331 | |
332 { | |
333 // Create a ZIP writer | |
334 HierarchicalZipWriter writer(tmp.GetPath().c_str()); | |
335 writer.SetZip64(isZip64); | |
336 writer.OpenDirectory("IMAGES"); | |
337 | |
338 // Create the DICOMDIR writer | |
339 DicomDirWriter dicomDir; | |
340 | |
341 // Retrieve the list of the instances | |
342 std::list<std::string> instances; | |
343 context.GetIndex().GetChildInstances(instances, id); | |
344 | |
345 size_t pos = 0; | |
346 for (std::list<std::string>::const_iterator | |
347 it = instances.begin(); it != instances.end(); ++it, ++pos) | |
348 { | |
349 // "DICOM restricts the filenames on DICOM media to 8 | |
350 // characters (some systems wrongly use 8.3, but this does not | |
351 // conform to the standard)." | |
352 std::string filename = "IM" + boost::lexical_cast<std::string>(pos); | |
353 writer.OpenFile(filename.c_str()); | |
354 | |
355 std::string dicom; | |
356 context.ReadFile(dicom, *it, FileContentType_Dicom); | |
357 writer.Write(dicom); | |
358 | |
359 ParsedDicomFile parsed(dicom); | |
360 dicomDir.Add("IMAGES", filename, parsed); | |
361 } | |
362 | |
363 // Add the DICOMDIR | |
364 writer.CloseDirectory(); | |
365 writer.OpenFile("DICOMDIR"); | |
366 std::string s; | |
367 dicomDir.Encode(s); | |
368 writer.Write(s); | |
369 } | |
370 | |
371 // Prepare the sending of the ZIP file | |
372 FilesystemHttpSender sender(tmp.GetPath()); | |
373 sender.SetContentType("application/zip"); | |
374 sender.SetContentFilename(id + ".zip"); | |
375 | |
376 // Send the ZIP | |
377 call.GetOutput().AnswerStream(sender); | |
378 | |
379 // The temporary file is automatically removed thanks to the RAII | |
380 } | |
381 | |
382 | |
383 | 78 |
384 | 79 |
385 namespace | 80 namespace |
386 { | 81 { |
387 class ResourceIdentifiers | 82 class ResourceIdentifiers |
724 const FileInfo& dicom) | 419 const FileInfo& dicom) |
725 { | 420 { |
726 out_ << " " << instanceId << std::endl; | 421 out_ << " " << instanceId << std::endl; |
727 } | 422 } |
728 }; | 423 }; |
424 | |
425 | |
426 class ArchiveWriterVisitor : public IArchiveVisitor | |
427 { | |
428 private: | |
429 HierarchicalZipWriter& writer_; | |
430 ServerContext& context_; | |
431 char instanceFormat_[24]; | |
432 unsigned int countInstances_; | |
433 | |
434 static std::string GetTag(const DicomMap& tags, | |
435 const DicomTag& tag) | |
436 { | |
437 const DicomValue* v = tags.TestAndGetValue(tag); | |
438 if (v != NULL && | |
439 !v->IsBinary() && | |
440 !v->IsNull()) | |
441 { | |
442 return v->GetContent(); | |
443 } | |
444 else | |
445 { | |
446 return ""; | |
447 } | |
448 } | |
449 | |
450 public: | |
451 ArchiveWriterVisitor(HierarchicalZipWriter& writer, | |
452 ServerContext& context) : | |
453 writer_(writer), | |
454 context_(context) | |
455 { | |
456 } | |
457 | |
458 virtual void Open(ResourceType level, | |
459 const std::string& publicId) | |
460 { | |
461 std::string path; | |
462 | |
463 DicomMap tags; | |
464 if (context_.GetIndex().GetMainDicomTags(tags, publicId, level, level)) | |
465 { | |
466 switch (level) | |
467 { | |
468 case ResourceType_Patient: | |
469 path = GetTag(tags, DICOM_TAG_PATIENT_ID) + " " + GetTag(tags, DICOM_TAG_PATIENT_NAME); | |
470 break; | |
471 | |
472 case ResourceType_Study: | |
473 path = GetTag(tags, DICOM_TAG_ACCESSION_NUMBER) + " " + GetTag(tags, DICOM_TAG_STUDY_DESCRIPTION); | |
474 break; | |
475 | |
476 case ResourceType_Series: | |
477 { | |
478 std::string modality = GetTag(tags, DICOM_TAG_MODALITY); | |
479 path = modality + " " + GetTag(tags, DICOM_TAG_SERIES_DESCRIPTION); | |
480 | |
481 if (modality.size() == 0) | |
482 { | |
483 snprintf(instanceFormat_, sizeof(instanceFormat_) - 1, "%%08d.dcm"); | |
484 } | |
485 else if (modality.size() == 1) | |
486 { | |
487 snprintf(instanceFormat_, sizeof(instanceFormat_) - 1, "%c%%07d.dcm", | |
488 toupper(modality[0])); | |
489 } | |
490 else if (modality.size() >= 2) | |
491 { | |
492 snprintf(instanceFormat_, sizeof(instanceFormat_) - 1, "%c%c%%06d.dcm", | |
493 toupper(modality[0]), toupper(modality[1])); | |
494 } | |
495 | |
496 countInstances_ = 0; | |
497 | |
498 break; | |
499 } | |
500 | |
501 default: | |
502 throw OrthancException(ErrorCode_InternalError); | |
503 } | |
504 } | |
505 | |
506 path = Toolbox::StripSpaces(Toolbox::ConvertToAscii(path)); | |
507 | |
508 if (path.empty()) | |
509 { | |
510 path = std::string("Unknown ") + EnumerationToString(level); | |
511 } | |
512 | |
513 writer_.OpenDirectory(path.c_str()); | |
514 } | |
515 | |
516 virtual void Close() | |
517 { | |
518 writer_.CloseDirectory(); | |
519 } | |
520 | |
521 virtual void AddInstance(const std::string& instanceId, | |
522 const FileInfo& dicom) | |
523 { | |
524 std::string content; | |
525 context_.ReadFile(content, dicom); | |
526 | |
527 char filename[24]; | |
528 snprintf(filename, sizeof(filename) - 1, instanceFormat_, countInstances_); | |
529 countInstances_ ++; | |
530 | |
531 writer_.OpenFile(filename); | |
532 writer_.Write(content); | |
533 } | |
534 | |
535 static void Apply(RestApiOutput& output, | |
536 ServerContext& context, | |
537 ArchiveIndex& archive, | |
538 const std::string& filename) | |
539 { | |
540 archive.Expand(context.GetIndex()); | |
541 | |
542 StatisticsVisitor stats; | |
543 archive.Apply(stats); | |
544 | |
545 const bool isZip64 = IsZip64Required(stats.GetUncompressedSize(), stats.GetInstancesCount()); | |
546 | |
547 // Create a RAII for the temporary file to manage the ZIP file | |
548 Toolbox::TemporaryFile tmp; | |
549 | |
550 { | |
551 // Create a ZIP writer | |
552 HierarchicalZipWriter writer(tmp.GetPath().c_str()); | |
553 writer.SetZip64(isZip64); | |
554 | |
555 ArchiveWriterVisitor v(writer, context); | |
556 archive.Apply(v); | |
557 } | |
558 | |
559 // Prepare the sending of the ZIP file | |
560 FilesystemHttpSender sender(tmp.GetPath()); | |
561 sender.SetContentType("application/zip"); | |
562 sender.SetContentFilename(filename); | |
563 | |
564 // Send the ZIP | |
565 output.AnswerStream(sender); | |
566 | |
567 // The temporary file is automatically removed thanks to the RAII | |
568 } | |
569 }; | |
570 | |
571 | |
572 class MediaWriterVisitor : public IArchiveVisitor | |
573 { | |
574 private: | |
575 HierarchicalZipWriter& writer_; | |
576 DicomDirWriter dicomDir_; | |
577 ServerContext& context_; | |
578 unsigned int countInstances_; | |
579 | |
580 public: | |
581 MediaWriterVisitor(HierarchicalZipWriter& writer, | |
582 ServerContext& context) : | |
583 writer_(writer), | |
584 context_(context), | |
585 countInstances_(0) | |
586 { | |
587 } | |
588 | |
589 void EncodeDicomDir(std::string& result) | |
590 { | |
591 dicomDir_.Encode(result); | |
592 } | |
593 | |
594 virtual void Open(ResourceType level, | |
595 const std::string& publicId) | |
596 { | |
597 } | |
598 | |
599 virtual void Close() | |
600 { | |
601 } | |
602 | |
603 virtual void AddInstance(const std::string& instanceId, | |
604 const FileInfo& dicom) | |
605 { | |
606 // "DICOM restricts the filenames on DICOM media to 8 | |
607 // characters (some systems wrongly use 8.3, but this does not | |
608 // conform to the standard)." | |
609 std::string filename = "IM" + boost::lexical_cast<std::string>(countInstances_); | |
610 writer_.OpenFile(filename.c_str()); | |
611 | |
612 std::string content; | |
613 context_.ReadFile(content, dicom); | |
614 writer_.Write(content); | |
615 | |
616 ParsedDicomFile parsed(content); | |
617 dicomDir_.Add("IMAGES", filename, parsed); | |
618 | |
619 countInstances_ ++; | |
620 } | |
621 | |
622 static void Apply(RestApiOutput& output, | |
623 ServerContext& context, | |
624 ArchiveIndex& archive, | |
625 const std::string& filename) | |
626 { | |
627 archive.Expand(context.GetIndex()); | |
628 | |
629 StatisticsVisitor stats; | |
630 archive.Apply(stats); | |
631 | |
632 const bool isZip64 = IsZip64Required(stats.GetUncompressedSize(), stats.GetInstancesCount()); | |
633 | |
634 // Create a RAII for the temporary file to manage the ZIP file | |
635 Toolbox::TemporaryFile tmp; | |
636 | |
637 { | |
638 // Create a ZIP writer | |
639 HierarchicalZipWriter writer(tmp.GetPath().c_str()); | |
640 writer.SetZip64(isZip64); | |
641 writer.OpenDirectory("IMAGES"); | |
642 | |
643 // Create the DICOMDIR writer | |
644 DicomDirWriter dicomDir; | |
645 | |
646 MediaWriterVisitor v(writer, context); | |
647 archive.Apply(v); | |
648 | |
649 // Add the DICOMDIR | |
650 writer.CloseDirectory(); | |
651 writer.OpenFile("DICOMDIR"); | |
652 std::string s; | |
653 v.EncodeDicomDir(s); | |
654 writer.Write(s); | |
655 } | |
656 | |
657 // Prepare the sending of the ZIP file | |
658 FilesystemHttpSender sender(tmp.GetPath()); | |
659 sender.SetContentType("application/zip"); | |
660 sender.SetContentFilename(filename); | |
661 | |
662 // Send the ZIP | |
663 output.AnswerStream(sender); | |
664 | |
665 // The temporary file is automatically removed thanks to the RAII | |
666 } | |
667 }; | |
729 } | 668 } |
730 | 669 |
731 | 670 |
732 static void CreateBatchArchive(RestApiPostCall& call) | 671 static bool AddResourcesOfInterest(ArchiveIndex& archive, |
672 RestApiPostCall& call) | |
733 { | 673 { |
734 ServerIndex& index = OrthancRestApi::GetIndex(call); | 674 ServerIndex& index = OrthancRestApi::GetIndex(call); |
735 | 675 |
736 Json::Value resources; | 676 Json::Value resources; |
737 if (call.ParseJsonRequest(resources) && | 677 if (call.ParseJsonRequest(resources) && |
738 resources.type() == Json::arrayValue) | 678 resources.type() == Json::arrayValue) |
739 { | 679 { |
740 ArchiveIndex archive(ResourceType_Patient); // root | |
741 | |
742 for (Json::Value::ArrayIndex i = 0; i < resources.size(); i++) | 680 for (Json::Value::ArrayIndex i = 0; i < resources.size(); i++) |
743 { | 681 { |
744 if (resources[i].type() != Json::stringValue) | 682 if (resources[i].type() != Json::stringValue) |
745 { | 683 { |
746 return; // Bad request | 684 return false; // Bad request |
747 } | 685 } |
748 | 686 |
749 ResourceIdentifiers resource(index, resources[i].asString()); | 687 ResourceIdentifiers resource(index, resources[i].asString()); |
750 archive.Add(index, resource); | 688 archive.Add(index, resource); |
751 } | 689 } |
752 | 690 |
753 archive.Expand(index); | 691 return true; |
754 | 692 } |
755 PrintVisitor v(std::cout); | 693 else |
756 archive.Apply(v); | 694 { |
757 | 695 return false; |
758 StatisticsVisitor s; | 696 } |
759 archive.Apply(s); | 697 } |
760 | 698 |
761 std::cout << s.GetUncompressedSize() << " " << s.GetInstancesCount() << std::endl; | 699 |
700 static void CreateBatchArchive(RestApiPostCall& call) | |
701 { | |
702 ArchiveIndex archive(ResourceType_Patient); // root | |
703 | |
704 if (AddResourcesOfInterest(archive, call)) | |
705 { | |
706 ArchiveWriterVisitor::Apply(call.GetOutput(), | |
707 OrthancRestApi::GetContext(call), | |
708 archive, | |
709 "Archive.zip"); | |
762 } | 710 } |
763 } | 711 } |
764 | 712 |
765 | 713 |
714 static void CreateBatchMedia(RestApiPostCall& call) | |
715 { | |
716 ArchiveIndex archive(ResourceType_Patient); // root | |
717 | |
718 if (AddResourcesOfInterest(archive, call)) | |
719 { | |
720 MediaWriterVisitor::Apply(call.GetOutput(), | |
721 OrthancRestApi::GetContext(call), | |
722 archive, | |
723 "Archive.zip"); | |
724 } | |
725 } | |
726 | |
727 | |
728 static void CreateArchive(RestApiGetCall& call) | |
729 { | |
730 ServerIndex& index = OrthancRestApi::GetIndex(call); | |
731 | |
732 std::string id = call.GetUriComponent("id", ""); | |
733 ResourceIdentifiers resource(index, id); | |
734 | |
735 ArchiveIndex archive(ResourceType_Patient); // root | |
736 archive.Add(OrthancRestApi::GetIndex(call), resource); | |
737 | |
738 ArchiveWriterVisitor::Apply(call.GetOutput(), | |
739 OrthancRestApi::GetContext(call), | |
740 archive, | |
741 id + ".zip"); | |
742 } | |
743 | |
744 | |
745 static void CreateMedia(RestApiGetCall& call) | |
746 { | |
747 ServerIndex& index = OrthancRestApi::GetIndex(call); | |
748 | |
749 std::string id = call.GetUriComponent("id", ""); | |
750 ResourceIdentifiers resource(index, id); | |
751 | |
752 ArchiveIndex archive(ResourceType_Patient); // root | |
753 archive.Add(OrthancRestApi::GetIndex(call), resource); | |
754 | |
755 MediaWriterVisitor::Apply(call.GetOutput(), | |
756 OrthancRestApi::GetContext(call), | |
757 archive, | |
758 id + ".zip"); | |
759 } | |
760 | |
766 | 761 |
767 void OrthancRestApi::RegisterArchive() | 762 void OrthancRestApi::RegisterArchive() |
768 { | 763 { |
769 Register("/patients/{id}/archive", GetArchive<ResourceType_Patient>); | 764 Register("/patients/{id}/archive", CreateArchive); |
770 Register("/studies/{id}/archive", GetArchive<ResourceType_Study>); | 765 Register("/studies/{id}/archive", CreateArchive); |
771 Register("/series/{id}/archive", GetArchive<ResourceType_Series>); | 766 Register("/series/{id}/archive", CreateArchive); |
772 | 767 |
773 Register("/patients/{id}/media", GetMediaArchive); | 768 Register("/patients/{id}/media", CreateMedia); |
774 Register("/studies/{id}/media", GetMediaArchive); | 769 Register("/studies/{id}/media", CreateMedia); |
775 Register("/series/{id}/media", GetMediaArchive); | 770 Register("/series/{id}/media", CreateMedia); |
776 | 771 |
777 Register("/tools/archive", CreateBatchArchive); | 772 Register("/tools/create-archive", CreateBatchArchive); |
773 Register("/tools/create-media", CreateBatchMedia); | |
778 } | 774 } |
779 } | 775 } |