Skip to content

Commit 2199ef2

Browse files
committed
Resolves smartstore#738 Implement download of pictures via URLs in product import
1 parent e495aa8 commit 2199ef2

11 files changed

Lines changed: 362 additions & 62 deletions

File tree

changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
* Elmar Shopinfo: Option to export delivery time as availability
4646
* #654 Place user agreement for downloadable files in checkout process
4747
* #398 EU law: add 'revocation' form and revocation waiver for ESD
48+
* #738 Implement download of pictures via URLs in product import
4849

4950
### Improvements
5051
* (Perf) Implemented static caches for URL aliases and localized properties. Increases app startup and request speed by up to 30%.

src/Libraries/SmartStore.Core/Domain/DataExchange/DataExchangeSettings.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ public class DataExchangeSettings : ISettings
77
public DataExchangeSettings()
88
{
99
MaxFileNameLength = 50;
10+
ImageDownloadTimeout = 10;
1011
}
1112

1213
/// <summary>
@@ -18,5 +19,10 @@ public DataExchangeSettings()
1819
/// Relative path to a folder with images to be imported
1920
/// </summary>
2021
public string ImageImportFolder { get; set; }
22+
23+
/// <summary>
24+
/// The timeout for image download per entity in minutes
25+
/// </summary>
26+
public int ImageDownloadTimeout { get; set; }
2127
}
2228
}

src/Libraries/SmartStore.Core/Logging/TraceLogger.cs

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public TraceLogger(string fileName)
2525
_traceSource = new TraceSource("SmartStore");
2626
_traceSource.Switch = new SourceSwitch("LogSwitch", "Error");
2727
_traceSource.Listeners.Remove("Default");
28-
28+
2929
var console = new ConsoleTraceListener(false);
3030
console.Filter = new EventTypeFilter(SourceLevels.All);
3131
console.Name = "console";
@@ -34,11 +34,15 @@ public TraceLogger(string fileName)
3434
textListener.Filter = new EventTypeFilter(SourceLevels.All);
3535
textListener.TraceOutputOptions = TraceOptions.DateTime;
3636

37-
// force UTF-8 encoding (even if the text just contains ANSI characters)
38-
var append = File.Exists(fileName);
37+
try
38+
{
39+
// force UTF-8 encoding (even if the text just contains ANSI characters)
40+
var append = File.Exists(fileName);
41+
_streamWriter = new StreamWriter(fileName, append, Encoding.UTF8);
3942

40-
_streamWriter = new StreamWriter(fileName, append, Encoding.UTF8);
41-
textListener.Writer = _streamWriter;
43+
textListener.Writer = _streamWriter;
44+
}
45+
catch (IOException) { }
4246

4347
_traceSource.Listeners.Add(console);
4448
_traceSource.Listeners.Add(textListener);
@@ -139,11 +143,13 @@ public void Flush()
139143
protected override void OnDispose(bool disposing)
140144
{
141145
_traceSource.Flush();
142-
143-
_streamWriter.Close();
144146
_traceSource.Close();
145147

146-
_streamWriter.Dispose();
148+
if (_streamWriter != null)
149+
{
150+
_streamWriter.Close();
151+
_streamWriter.Dispose();
152+
}
147153
}
148154
}
149155
}

src/Libraries/SmartStore.Core/Utilities/FileDownloadManager.cs

Lines changed: 71 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,32 @@ public FileDownloadResponse DownloadFile(string url, bool sendAuthCookie = false
5151
}
5252

5353
using (var resp = (HttpWebResponse)req.GetResponse())
54+
using (var stream = resp.GetResponseStream())
5455
{
55-
using (var stream = resp.GetResponseStream())
56+
if (resp.StatusCode == HttpStatusCode.OK)
5657
{
57-
if (resp.StatusCode == HttpStatusCode.OK)
58+
var data = stream.ToByteArray();
59+
if (data != null && data.Length != 0)
5860
{
59-
var data = stream.ToByteArray();
60-
var cd = new ContentDisposition(resp.Headers["Content-Disposition"]);
61-
var fileName = cd.FileName;
61+
string fileName = null;
62+
63+
var contentDisposition = resp.Headers["Content-Disposition"];
64+
if (contentDisposition.HasValue())
65+
{
66+
var cd = new ContentDisposition(contentDisposition);
67+
fileName = cd.FileName;
68+
}
69+
70+
if (fileName.IsEmpty())
71+
{
72+
try
73+
{
74+
var uri = new Uri(url);
75+
fileName = Path.GetFileName(uri.LocalPath);
76+
}
77+
catch { }
78+
}
79+
6280
return new FileDownloadResponse(data, fileName, resp.ContentType);
6381
}
6482
}
@@ -79,39 +97,51 @@ public async Task DownloadAsync(FileDownloadManagerContext context, IEnumerable<
7997

8098
private async Task DownloadFiles(FileDownloadManagerContext context, IEnumerable<FileDownloadManagerItem> items)
8199
{
82-
var client = new HttpClient();
83-
84-
client.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue();
85-
client.DefaultRequestHeaders.CacheControl.NoCache = true;
86-
client.DefaultRequestHeaders.Add("Connection", "Keep-alive");
100+
try
101+
{
102+
using (var client = new HttpClient())
103+
{
104+
client.DefaultRequestHeaders.CacheControl = new CacheControlHeaderValue();
105+
client.DefaultRequestHeaders.CacheControl.NoCache = true;
106+
client.DefaultRequestHeaders.Add("Connection", "Keep-alive");
87107

88-
if (context.Timeout.TotalMilliseconds > 0 && context.Timeout != Timeout.InfiniteTimeSpan)
89-
client.Timeout = context.Timeout;
108+
if (context.Timeout.TotalMilliseconds > 0 && context.Timeout != Timeout.InfiniteTimeSpan)
109+
client.Timeout = context.Timeout;
90110

91-
IEnumerable<Task> downloadTasksQuery =
92-
from item in items
93-
select ProcessUrl(context, client, item);
111+
IEnumerable<Task> downloadTasksQuery =
112+
from item in items
113+
select ProcessUrl(context, client, item);
94114

95-
// now execute the bunch
96-
List<Task> downloadTasks = downloadTasksQuery.ToList();
115+
// now execute the bunch
116+
List<Task> downloadTasks = downloadTasksQuery.ToList();
97117

98-
while (downloadTasks.Count > 0)
99-
{
100-
// identify the first task that completes
101-
Task firstFinishedTask = await Task.WhenAny(downloadTasks);
118+
while (downloadTasks.Count > 0)
119+
{
120+
// identify the first task that completes
121+
Task firstFinishedTask = await Task.WhenAny(downloadTasks);
102122

103-
// process only once
104-
downloadTasks.Remove(firstFinishedTask);
123+
// process only once
124+
downloadTasks.Remove(firstFinishedTask);
105125

106-
await firstFinishedTask;
126+
await firstFinishedTask;
127+
}
128+
}
129+
}
130+
catch (Exception exception)
131+
{
132+
if (context.Logger != null)
133+
context.Logger.ErrorsAll(exception);
107134
}
108135
}
109136

110137
private async Task ProcessUrl(FileDownloadManagerContext context, HttpClient client, FileDownloadManagerItem item)
111138
{
112139
try
113140
{
114-
Task<Stream> task = client.GetStreamAsync(item.Url);
141+
//HttpResponseMessage response = await client.GetAsync(item.Url, HttpCompletionOption.ResponseHeadersRead);
142+
//Task<Stream> task = response.Content.ReadAsStreamAsync();
143+
144+
Task <Stream> task = client.GetStreamAsync(item.Url);
115145
await task;
116146

117147
int count;
@@ -132,29 +162,31 @@ private async Task ProcessUrl(FileDownloadManagerContext context, HttpClient cli
132162

133163
item.Success = (!task.IsFaulted && !canceled);
134164
}
135-
catch (Exception exc)
165+
catch (Exception exception)
136166
{
137-
item.Success = false;
138-
item.ErrorMessage = exc.ToAllMessages();
139-
140-
var webExc = exc.InnerException as WebException;
141-
if (webExc != null)
142-
item.ExceptionStatus = webExc.Status;
167+
try
168+
{
169+
item.Success = false;
170+
item.ErrorMessage = exception.ToAllMessages();
143171

144-
if (context.Logger != null)
145-
context.Logger.Error(item.ToString(), exc);
172+
var webExc = exception.InnerException as WebException;
173+
if (webExc != null)
174+
item.ExceptionStatus = webExc.Status;
175+
176+
if (context.Logger != null)
177+
context.Logger.Error(item.ToString(), exception);
178+
}
179+
catch { }
146180
}
147181
}
148-
149182
}
150183

184+
151185
public class FileDownloadResponse
152186
{
153187
public FileDownloadResponse(byte[] data, string fileName, string contentType)
154188
{
155189
Guard.ArgumentNotNull(() => data);
156-
Guard.ArgumentNotEmpty(() => fileName);
157-
Guard.ArgumentNotEmpty(() => contentType);
158190

159191
this.Data = data;
160192
this.FileName = fileName;
@@ -182,12 +214,12 @@ public class FileDownloadManagerContext
182214
/// <summary>
183215
/// Optional logger to log errors
184216
/// </summary>
185-
public TraceLogger Logger { get; set; }
217+
public ILogger Logger { get; set; }
186218

187219
/// <summary>
188220
/// Cancellation token
189221
/// </summary>
190-
public CancellationTokenSource CancellationToken { get; set; }
222+
public CancellationToken CancellationToken { get; set; }
191223

192224
/// <summary>
193225
/// Timeout for the HTTP client

src/Libraries/SmartStore.Data/Migrations/201601262000441_ImportFramework1.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,12 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder)
397397
"Bilderordner (relativer Pfad)",
398398
"Specifies a relative path to a folder with images to be imported (e.g. Content\\Images).",
399399
"Legt einen relativen Pfad zu einem Ordner mit zu importierenden Bildern fest (z.B. Inhalt\\Bilder).");
400+
401+
builder.AddOrUpdate("Admin.Configuration.Settings.DataExchange.ImageDownloadTimeout",
402+
"Timeout for image download (minutes)",
403+
"Zeitlimit für Bilder-Download (Minuten)",
404+
"Specifies the timeout for the image download in minutes.",
405+
"Legt das Zeitlimit für den Bilder-Download in Minuten fest.");
400406
}
401407
}
402408
}

0 commit comments

Comments
 (0)