-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathEcbHttpDownloader.cs
More file actions
229 lines (193 loc) · 7.9 KB
/
EcbHttpDownloader.cs
File metadata and controls
229 lines (193 loc) · 7.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;
namespace EcbFxExchangeRate.SSIS.ScriptTasks
{
/*
--------------------------------------------------------------------
Classe : EcbHttpDownloader
--------------------------------------------------------------------
Objectif :
Télécharger un fichier distant (ex : dataset ECB) via HTTP
et l’enregistrer sur le disque local.
Cette classe est utilisée dans une Script Task SSIS afin
d'isoler la logique réseau du ScriptMain.cs.
Avantages :
- code plus lisible
- logique réutilisable
- meilleure maintenabilité du package SSIS
--------------------------------------------------------------------
*/
internal sealed class EcbHttpDownloader
{
/*
Méthode principale de téléchargement.
url : adresse HTTP du fichier à télécharger
downloadPath : chemin local où enregistrer le fichier
Le téléchargement est asynchrone afin de ne pas bloquer
inutilement le thread de la Script Task.
*/
public async Task DownloadAsync(string url, string downloadPath)
{
// Vérifie la validité des paramètres fournis
ValidateInputs(url, downloadPath);
// Configure les paramètres de sécurité réseau (.NET)
ConfigureSecurity();
// Vérifie que le dossier cible existe
EnsureTargetDirectoryExists(downloadPath);
/*
Création du HttpClient.
HttpClient est la classe standard .NET pour faire
des appels HTTP (API REST, téléchargement de fichiers, etc.).
*/
using (var http = CreateHttpClient())
/*
GetAsync avec ResponseHeadersRead :
- permet de commencer la lecture du flux immédiatement
- évite de charger tout le fichier en mémoire
- idéal pour les téléchargements de fichiers (pattern streaming)
*/
using (HttpResponseMessage response = await http
.GetAsync(url, HttpCompletionOption.ResponseHeadersRead)
.ConfigureAwait(false))
{
/*
Vérification du statut HTTP.
Si la requête retourne une erreur (404, 500, etc.)
on lève explicitement une exception détaillée.
*/
if (!response.IsSuccessStatusCode)
{
// Lecture partielle du contenu retourné (si disponible)
string bodySnippet = await SafeReadSnippetAsync(response).ConfigureAwait(false);
throw new HttpRequestException(
"HTTP " + (int)response.StatusCode +
" (" + response.ReasonPhrase + "). " +
"URL=" + url + ". " +
"Réponse (extrait)=" + bodySnippet);
}
/*
Lecture du flux HTTP sous forme de Stream.
Cela permet de traiter le fichier en streaming
sans le charger entièrement en mémoire.
*/
using (Stream contentStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false))
/*
Création du fichier cible.
FileMode.Create :
- écrase le fichier s’il existe déjà
*/
using (FileStream fileStream = new FileStream(
downloadPath,
FileMode.Create,
FileAccess.Write,
FileShare.None))
{
/*
Copie du flux HTTP vers le fichier.
Pattern classique :
Stream HTTP -> FileStream disque
*/
await contentStream.CopyToAsync(fileStream).ConfigureAwait(false);
}
}
}
/*
Vérifie les paramètres d'entrée.
Dans un contexte ETL, valider les entrées évite
des erreurs difficiles à diagnostiquer dans SSIS.
*/
private static void ValidateInputs(string url, string downloadPath)
{
if (string.IsNullOrWhiteSpace(url))
throw new ArgumentException("Le paramètre url est vide.");
if (string.IsNullOrWhiteSpace(downloadPath))
throw new ArgumentException("Le paramètre downloadPath est vide.");
}
/*
Configuration du protocole de sécurité réseau.
TLS 1.2 est requis par la plupart des API modernes
(dont les services de la Banque Centrale Européenne).
*/
private static void ConfigureSecurity()
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
// Désactive une optimisation HTTP peu utile ici
ServicePointManager.Expect100Continue = false;
}
/*
Vérifie que le dossier cible existe.
Si le dossier n'existe pas, il est créé automatiquement.
Cela évite les erreurs "Directory not found".
*/
private static void EnsureTargetDirectoryExists(string downloadPath)
{
string dir = Path.GetDirectoryName(downloadPath);
if (!string.IsNullOrWhiteSpace(dir) && !Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
}
/*
Création et configuration du client HTTP.
Bonnes pratiques appliquées :
- timeout défini
- headers Accept explicites
- User-Agent identifié
*/
private static HttpClient CreateHttpClient()
{
var http = new HttpClient
{
// Temps maximum d’attente de la requête
Timeout = TimeSpan.FromSeconds(60)
};
/*
Headers HTTP Accept
On indique au serveur les formats que l'on accepte.
Ici principalement CSV ou texte brut.
*/
http.DefaultRequestHeaders.Accept.Clear();
http.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("text/csv"));
http.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("text/plain"));
/*
User-Agent
Permet au serveur d’identifier le client.
Utile pour :
- debugging
- logs serveur
*/
http.DefaultRequestHeaders.UserAgent.ParseAdd("SSIS-ScriptTask/1.0");
return http;
}
/*
Lecture sécurisée du contenu retourné par le serveur.
Si une erreur HTTP se produit, on tente de lire
une partie du contenu pour faciliter le diagnostic.
La lecture est limitée à 300 caractères afin
d'éviter des messages d'erreur trop volumineux.
*/
private static async Task<string> SafeReadSnippetAsync(HttpResponseMessage response)
{
try
{
string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
if (string.IsNullOrEmpty(content))
return "<vide>";
return content.Length <= 300
? content
: content.Substring(0, 300) + "...";
}
catch
{
// Si le contenu ne peut pas être lu
return "<impossible de lire le contenu>";
}
}
}
}