Mercurial > hg > orthanc
comparison OrthancFramework/Sources/FileStorage/FilesystemStorage.cpp @ 5807:8279eaab0d1d attach-custom-data
merged default -> attach-custom-data
author | Alain Mazy <am@orthanc.team> |
---|---|
date | Tue, 24 Sep 2024 11:39:52 +0200 |
parents | d7274e43ea7c 2fe77dfe0466 |
children |
comparison
equal
deleted
inserted
replaced
5085:79f98ee4f04b | 5807:8279eaab0d1d |
---|---|
1 /** | 1 /** |
2 * Orthanc - A Lightweight, RESTful DICOM Store | 2 * Orthanc - A Lightweight, RESTful DICOM Store |
3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics | 3 * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics |
4 * Department, University Hospital of Liege, Belgium | 4 * Department, University Hospital of Liege, Belgium |
5 * Copyright (C) 2017-2022 Osimis S.A., Belgium | 5 * Copyright (C) 2017-2023 Osimis S.A., Belgium |
6 * Copyright (C) 2021-2022 Sebastien Jodogne, ICTEAM UCLouvain, Belgium | 6 * Copyright (C) 2024-2024 Orthanc Team SRL, Belgium |
7 * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium | |
7 * | 8 * |
8 * This program is free software: you can redistribute it and/or | 9 * This program is free software: you can redistribute it and/or |
9 * modify it under the terms of the GNU Lesser General Public License | 10 * modify it under the terms of the GNU Lesser General Public License |
10 * as published by the Free Software Foundation, either version 3 of | 11 * as published by the Free Software Foundation, either version 3 of |
11 * the License, or (at your option) any later version. | 12 * the License, or (at your option) any later version. |
21 **/ | 22 **/ |
22 | 23 |
23 | 24 |
24 #include "../PrecompiledHeaders.h" | 25 #include "../PrecompiledHeaders.h" |
25 #include "FilesystemStorage.h" | 26 #include "FilesystemStorage.h" |
27 #include <boost/thread.hpp> | |
26 | 28 |
27 // http://stackoverflow.com/questions/1576272/storing-large-number-of-files-in-file-system | 29 // http://stackoverflow.com/questions/1576272/storing-large-number-of-files-in-file-system |
28 // http://stackoverflow.com/questions/446358/storing-a-large-number-of-images | 30 // http://stackoverflow.com/questions/446358/storing-a-large-number-of-images |
29 | 31 |
30 #include "../Logging.h" | 32 #include "../Logging.h" |
120 void FilesystemStorage::Create(const std::string& uuid, | 122 void FilesystemStorage::Create(const std::string& uuid, |
121 const void* content, | 123 const void* content, |
122 size_t size, | 124 size_t size, |
123 FileContentType type) | 125 FileContentType type) |
124 { | 126 { |
127 Toolbox::ElapsedTimer timer; | |
125 LOG(INFO) << "Creating attachment \"" << uuid << "\" of \"" << GetDescriptionInternal(type) | 128 LOG(INFO) << "Creating attachment \"" << uuid << "\" of \"" << GetDescriptionInternal(type) |
126 << "\" type (size: " << (size / (1024 * 1024) + 1) << "MB)"; | 129 << "\" type"; |
127 | 130 |
128 boost::filesystem::path path; | 131 boost::filesystem::path path; |
129 | 132 |
130 path = GetPath(uuid); | 133 path = GetPath(uuid); |
131 | 134 |
132 if (boost::filesystem::exists(path)) | 135 if (boost::filesystem::exists(path)) |
133 { | 136 { |
134 // Extremely unlikely case: This Uuid has already been created | 137 // Extremely unlikely case: This Uuid has already been created |
135 // in the past. | 138 // in the past. |
136 throw OrthancException(ErrorCode_InternalError); | 139 throw OrthancException(ErrorCode_InternalError, "This file UUID already exists"); |
137 } | 140 } |
138 | 141 |
139 if (boost::filesystem::exists(path.parent_path())) | 142 // In very unlikely cases, a thread could be deleting a |
140 { | 143 // directory while another thread needs it -> introduce 3 retries at 1 ms interval |
141 if (!boost::filesystem::is_directory(path.parent_path())) | 144 int retryCount = 0; |
142 { | 145 const int maxRetryCount = 3; |
143 throw OrthancException(ErrorCode_DirectoryOverFile); | 146 |
144 } | 147 while (retryCount < maxRetryCount) |
145 } | 148 { |
146 else | 149 retryCount++; |
147 { | 150 if (retryCount > 1) |
148 if (!boost::filesystem::create_directories(path.parent_path())) | 151 { |
149 { | 152 boost::this_thread::sleep(boost::posix_time::milliseconds(2 * retryCount + (rand() % 10))); |
150 throw OrthancException(ErrorCode_FileStorageCannotWrite); | 153 LOG(INFO) << "Retrying to create attachment \"" << uuid << "\" of \"" << GetDescriptionInternal(type) |
151 } | 154 << "\" type"; |
152 } | 155 } |
153 | 156 |
154 SystemToolbox::WriteFile(content, size, path.string(), fsyncOnWrite_); | 157 try |
158 { | |
159 boost::filesystem::create_directories(path.parent_path()); // the function ensures that the directory exists or throws | |
160 } | |
161 catch (boost::filesystem::filesystem_error& er) | |
162 { | |
163 if (er.code() == boost::system::errc::file_exists // the last element of the parent_path is a file | |
164 || er.code() == boost::system::errc::not_a_directory) // one of the element of the parent_path is not a directory | |
165 { | |
166 throw OrthancException(ErrorCode_DirectoryOverFile, "One of the element of the path is a file"); // no need to retry this error | |
167 } | |
168 | |
169 // ignore other errors and retry | |
170 } | |
171 | |
172 try | |
173 { | |
174 SystemToolbox::WriteFile(content, size, path.string(), fsyncOnWrite_); | |
175 | |
176 LOG(INFO) << "Created attachment \"" << uuid << "\" (" << timer.GetHumanTransferSpeed(true, size) << ")"; | |
177 return; | |
178 } | |
179 catch (OrthancException& ex) | |
180 { | |
181 if (retryCount >= maxRetryCount) | |
182 { | |
183 throw ex; | |
184 } | |
185 } | |
186 } | |
155 } | 187 } |
156 | 188 |
157 | 189 |
158 IMemoryBuffer* FilesystemStorage::Read(const std::string& uuid, | 190 IMemoryBuffer* FilesystemStorage::Read(const std::string& uuid, |
159 FileContentType type) | 191 FileContentType type) |
160 { | 192 { |
193 Toolbox::ElapsedTimer timer; | |
161 LOG(INFO) << "Reading attachment \"" << uuid << "\" of \"" << GetDescriptionInternal(type) | 194 LOG(INFO) << "Reading attachment \"" << uuid << "\" of \"" << GetDescriptionInternal(type) |
162 << "\" content type"; | 195 << "\" content type"; |
163 | 196 |
164 std::string content; | 197 std::string content; |
165 SystemToolbox::ReadFile(content, GetPath(uuid).string()); | 198 SystemToolbox::ReadFile(content, GetPath(uuid).string()); |
199 | |
200 LOG(INFO) << "Read attachment \"" << uuid << "\" (" << timer.GetHumanTransferSpeed(true, content.size()) << ")"; | |
166 | 201 |
167 return StringMemoryBuffer::CreateFromSwap(content); | 202 return StringMemoryBuffer::CreateFromSwap(content); |
168 } | 203 } |
169 | 204 |
170 | 205 |
171 IMemoryBuffer* FilesystemStorage::ReadRange(const std::string& uuid, | 206 IMemoryBuffer* FilesystemStorage::ReadRange(const std::string& uuid, |
172 FileContentType type, | 207 FileContentType type, |
173 uint64_t start /* inclusive */, | 208 uint64_t start /* inclusive */, |
174 uint64_t end /* exclusive */) | 209 uint64_t end /* exclusive */) |
175 { | 210 { |
211 Toolbox::ElapsedTimer timer; | |
176 LOG(INFO) << "Reading attachment \"" << uuid << "\" of \"" << GetDescriptionInternal(type) | 212 LOG(INFO) << "Reading attachment \"" << uuid << "\" of \"" << GetDescriptionInternal(type) |
177 << "\" content type (range from " << start << " to " << end << ")"; | 213 << "\" content type (range from " << start << " to " << end << ")"; |
178 | 214 |
179 std::string content; | 215 std::string content; |
180 SystemToolbox::ReadFileRange( | 216 SystemToolbox::ReadFileRange( |
181 content, GetPath(uuid).string(), start, end, true /* throw if overflow */); | 217 content, GetPath(uuid).string(), start, end, true /* throw if overflow */); |
182 | 218 |
219 LOG(INFO) << "Read range of attachment \"" << uuid << "\" (" << timer.GetHumanTransferSpeed(true, content.size()) << ")"; | |
183 return StringMemoryBuffer::CreateFromSwap(content); | 220 return StringMemoryBuffer::CreateFromSwap(content); |
184 } | 221 } |
185 | 222 |
186 | 223 |
187 bool FilesystemStorage::HasReadRange() const | 224 bool FilesystemStorage::HasReadRange() const |