
var inboxFiles = new List<FileGeneric>();
var request = CreateRequest("https://ext.transfertpro.com/api/v5/Inbox/", "application/json", HttpMethod.Get);
try
{
using (HttpResponseMessage response = GetHttpClient().SendAsync(request).Result)
{
if (response.IsSuccessStatusCode)
{
string json = response.Content.ReadAsStringAsync().Result;
var results = JsonConvert.DeserializeObject<IEnumerable<dynamic>>(json);
foreach (dynamic result in results)
{
inboxFiles.Add(new FileGeneric() { Id = result.gValue,
FileName = result.name,
FileSize = result.fileSize,
DateComplete = result.date,
Checksum = result.checksum,
IsShadow = result.shadow });
}
}
else
{
// Gérer les codes status 400, 401, 403...
}
}
request.Dispose();
}
catch (Exception ex)
{
// Gérer les exceptions
}
return inboxFiles;
| Nom du paramètre | Obligatoire | Description | Type |
| i | Oui | Id du fichier | string |
| n | Oui | Nom du fichier | string |
| s | Non | Mot de passe du fichier si l’envoi a été fait avec le mode ‘Shadow’ activé | string |
//Construction de l’url
var requestUrl = "https://dl.transfertpro.com/download/inbox?";
requestUrl += "i=" + fileToDownloadId;
requestUrl += "&n=" + fileToDownloadName;
requestUrl += "&s=" + fileToDownloadShadow;
//Instanciation de la requête
var request = new HttpRequestMessage(HttpMethod.Get, requestUrl);
try
{
//Envoi de la requête
var response = GetHttpClient().SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ContinueWith(task =>
{
var responseMessage = task.Result;
if(responseMessage.IsSuccessStatusCode)
{
using (var responseStream = responseMessage.Content.ReadAsStreamAsync().Result)
{
// Lire le flux en lecture sur le fichier
}
}
else
{
// Gérer les codes status 400, 401, 403...
}
});
response.Wait();
}
catch (Exception ex)
{
// Gérer les exceptions
}
request.Dispose();
| Nom du paramètre | Obligatoire | Description | Type |
| i | Oui | Id du fichier | string |
| n | Oui | Nom du fichier | string |
| s | Non | Id du partage. Nécessaire si le fichier se trouve dans un partage. | string |
//Construction de l’url
var requestUrl = "https://dl.transfertpro.com/download/myfile?";
requestUrl += "i=" + fileToDownloadId;
requestUrl += "&n=" + fileToDownloadName;
requestUrl += "&s=" + fileToDownloadShareId;
//Instanciation de la requête
var request = new HttpRequestMessage(HttpMethod.Get, requestUrl);
try
{
//Envoi de la requête
var response = GetHttpClient().SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ContinueWith(task =>
{
var responseMessage = task.Result;
if(responseMessage.IsSuccessStatusCode)
{
using (var responseStream = responseMessage.Content.ReadAsStreamAsync().Result)
{
// Lire le flux en lecture sur le fichier
}
}
else
{
// Gérer les codes status 400, 401, 403...
}
});
response.Wait();
}
catch (Exception ex)
{
// Gérer les exceptions
}
request.Dispose();
//Construction de l’url
var requestUrl = "https://ext.transfertpro.com/api/v5/File";
DraftFileDto file = new DraftFileDto();
file.UploadId = Guid.NewGuid();
file.FileName = "test.doc";
file.FileSize = 1204;
file.DirectoryId = "303180a3-7b28-41f8-9c5e-39b9952225e0";
file.ModificationId = "";
bool result = false;
try
{
using (HttpResponseMessage response = PostHttpClient().PostAsJsonAsync(requestUrl, file).Result)
{
ProcessResponse(response,
() => { result = true; },
() =>
{
log.WarnFormat("Impossible de créer le draft. Statut http : {0}", (int)response.StatusCode);
log.WarnFormat(" Contenu de la réponse : {0}", response.Content.ReadAsStringAsync().Result);
});
}
}
catch (AggregateException ex)
{
log.Error(string.Format("Impossible de créer le draft"), ex);
//Utiliser cette erreur pour mettre le programme dans un statut "erreur réseau" afin de dire que le serveur n'est pas joignable.
ThrowConnectionError();
}
//Construction de l’url
var requestUrl = "https://ext.transfertpro.com/api/v5/File/";
DraftFileDto file = new DraftFileDto();
file.UploadId = Guid.NewGuid();
file.FileName = "test.doc";
file.FileSize = 1204;
file.DirectoryId = "303180a3-7b28-41f8-9c5e-39b9952225e0";
file.ModificationId = "";
var shareId = "3904fada-8d14-4d27-b0bd-c1a693b8603a";
bool result = false;
var suffixUrl = string.Format("share/{0}", shareId);
try
{
using (HttpResponseMessage response = PostHttpClient().PostAsJsonAsync(requestUrl + suffixUrl, file).Result)
{
ProcessResponse(response,
() => { result = true; },
() =>
{
log.WarnFormat("Impossible de créer le draft. Statut http : {0}", (int)response.StatusCode);
log.WarnFormat(" Contenu de la réponse : {0}", response.Content.ReadAsStringAsync().Result);
});
}
}
catch (AggregateException ex)
{
log.Error(string.Format("Impossible de créer le draft"), ex);
//Utiliser cette erreur pour mettre le programme dans un statut "erreur réseau" afin de dire que le serveur n'est pas joignable.
ThrowConnectionError();
}
// L'envoi des données ne se fait pas sur le même hôte que le reste de l'API :
// il faut viser https://up.transfertpro.com/Chunk (ext.transfertpro.com ne connaît
// pas cette route). L'URL est donnée en absolu pour ne pas dépendre du BaseAddress
// du HttpClient.
const string uploadUrl = "https://up.transfertpro.com/Chunk";
// L'upload se fait par chunk de 8 Mo (8388608 octets) : un fichier de plus de 8 Mo
// demande plusieurs appels, en indiquant quel morceau est en cours d'envoi.
// Comme tout appel à l'API, la requête doit porter l'en-tête Authorization : Bearer
// ET les trois paramètres de signature en querystring (apiKeyName, nonce, hashkey),
// calculés comme décrit sur la page « Signature Requêtes ».
//
// C'est le jeton qui identifie l'auteur du dépôt : envoyez-le systématiquement, et ne
// vous reposez pas sur le paramètre « sender » pour porter cette identité.
//
// S'y ajoutent les métadonnées du chunk :
//
// - uid [GUID] : l'identifiant d'upload du fichier, défini à l'étape 1 comme UploadId
// - name [string] : le nom du fichier
// - chunk [integer] : l'index du chunk en cours d'envoi. Le premier chunk est l'index 0
// - chunks [integer] (min = 1) : le nombre total de chunks de ce fichier
// - share [GUID] : l'identifiant du partage. Hors partage (Mes Fichiers), laissez le
// paramètre vide : toute valeur qui n'est pas un GUID, « 0 » comprise,
// est traitée comme une absence de partage
// - offset [integer] : le nombre d'octets déjà envoyés. 0 lors du premier chunk. Le
// service l'accepte mais ne l'utilise pas pour placer les données :
// chaque chunk est repéré par son index
// - sender [string] : nom d'utilisateur de l'émetteur. Renseignez-le avec le compte
// auquel appartient le jeton porté par la requête. Une valeur qui
// diffère de ce compte n'est honorée que si son titulaire vous a
// accordé une délégation ; sinon elle est ignorée
// - dest [GUID] : dossier de destination. Facultatif : le dossier est déjà porté par
// le brouillon créé à l'étape 1, via sa propriété DirectoryId
//
// Écrasement : déposer un fichier portant le nom d'un fichier existant remplace son
// contenu et conserve son identifiant. C'est le comportement constaté quelle que soit
// la valeur transmise pour le paramètre d'écrasement : ne comptez pas sur ce paramètre
// pour obtenir un refus en cas de doublon. Si vous avez besoin de ce contrôle,
// testez l'existence au préalable avec File/Directories/{directoryId}/ExistFile/{fileName}.
string fileId = null; // UploadId défini à l'étape 1
string fileName = null; // nom du fichier
string username = null; // nom d'utilisateur de l'émetteur
string shadow = null; // mot de passe de chiffrement, si le fichier est protégé
string apiKeyName = null; // votre clé publique
string privateApiKeyValue = null; // votre clé privée, jamais en dur dans le code
int chunkTotal = 1; // nombre total de chunks du fichier
long rangeStart = 0; // octets déjà envoyés
byte[] fileContentBuffer = null; // contenu du chunk courant
bool isUploaded = false;
try
{
for (int chunkIndex = 0; chunkIndex < chunkTotal; chunkIndex++)
{
// Remplir ici fileContentBuffer avec le chunk courant, lu depuis le fichier source.
using (var content = new MultipartFormDataContent())
{
// nonce et hashkey sont recalculés à chaque chunk : le serveur refuse un
// nonce déjà utilisé. GetHashKey est le helper de la page « Signature Requêtes ».
var signatureParameters = new Dictionary<string, string>();
signatureParameters["apiKeyName"] = apiKeyName;
signatureParameters["nonce"] = Guid.NewGuid().ToString();
string hashkey = GetHashKey(signatureParameters, privateApiKeyValue);
string queryString = string.Format("?uid={0}&name={1}&chunk={2}&chunks={3}&share={4}&offset={5}&sender={6}&apiKeyName={7}&nonce={8}&hashkey={9}",
fileId,
Uri.EscapeDataString(fileName),
chunkIndex,
chunkTotal,
0, // 0 = hors partage (Mes Fichiers) ; sinon l'identifiant du partage
rangeStart,
Uri.EscapeDataString(username),
Uri.EscapeDataString(apiKeyName),
signatureParameters["nonce"],
hashkey);
var fileContent = new ByteArrayContent(fileContentBuffer);
// Nommez explicitement les parties du multipart. Le serveur accepte aussi
// une partie non nommée, mais il se rabat alors sur l'ordre des parties.
content.Add(fileContent, "file", fileName);
if (!string.IsNullOrEmpty(shadow))
content.Add(new StringContent(shadow), "shadow");
using (HttpResponseMessage response = GetHttpClient().PostAsync(uploadUrl + queryString, content).Result)
{
if (response.IsSuccessStatusCode)
{
isUploaded = true;
rangeStart += fileContentBuffer.Length;
}
else
{
// Gérer les codes status 400, 401, 403...
}
}
}
}
}
catch (Exception ex)
{
// Gérer les exceptions
}
// Une fois le dernier chunk acquitté (HTTP 200), le fichier est disponible dans le
// dossier cible : aucun appel de finalisation n'est nécessaire.
La première étape ne change pas : créez le brouillon en déclarant le fichier, avec son
UploadId, son nom et sa taille réelle — c'est cette taille que
verra le destinataire. Récupérez les trois valeurs dans un listage de dossier : le champ
Id d'une entrée de Files est l'identifiant à transmettre, aux côtés
de FileName et FileSize.
La deuxième étape se réduit alors à un seul appel, quelle que soit la taille du fichier :
// Deux parametres s'ajoutent a ceux de l'etape 2 decrite plus haut :
//
// - fromTBox [bool] : true pour demander le rattachement au lieu d'un depot d'octets
// - fileIdTbox [GUID] : identifiant du fichier source deja stocke, soit le champ Id
// d'une entree de Files dans un listage de dossier
//
// Les parametres habituels restent OBLIGATOIRES et sont lus AVANT fromTBox : uid (le
// UploadId du brouillon), name, chunk et chunks. Un seul appel suffit, la boucle de
// l'etape 2 n'a pas lieu d'etre ici : renseignez chunk a 0 et chunks a 1.
//
// La VALEUR de chunks n'influe que sur le cas de rechiffrement decrit plus bas ; elle
// doit simplement etre presente et numerique. Inutile de calculer le decoupage du
// fichier source pour le cas courant.
//
// Le corps doit rester un multipart valide bien que son contenu ne soit pas lu sur ce
// chemin : envoyez une partie vide.
//
// La requete doit etre AUTHENTIFIEE (en-tete Authorization : Bearer). Contrairement au
// depot d'octets, le renvoi depuis le stockage n'accepte pas d'appel anonyme.
// `sender` n'est volontairement PAS transmis : sur ce chemin l'uploadeur est resolu
// depuis le jeton, et ce parametre n'y est jamais lu. Le renseigner serait sans effet.
string queryString = string.Format("?uid={0}&name={1}&chunk=0&chunks=1"
+ "&fromTBox=true&fileIdTbox={2}&apiKeyName={3}&nonce={4}&hashkey={5}",
uploadId, // UploadId declare a l'etape 1
Uri.EscapeDataString(fileName),
fileIdTbox, // champ Id du fichier source
Uri.EscapeDataString(apiKeyName),
signatureParameters["nonce"],
hashkey);
using (var content = new MultipartFormDataContent())
{
content.Add(new ByteArrayContent(new byte[0]), "file", fileName);
using (HttpResponseMessage response = GetHttpClient().PostAsync(uploadUrl + queryString, content).Result)
{
// 200 : les blocs sont rattaches et le fichier du brouillon est termine.
// 403 : le fichier source n'existe pas, ou vous n'avez pas le droit de le lire.
// Les deux cas partagent volontairement la meme reponse.
// 403 : le brouillon designe par uid n'est pas le votre, ou il est deja termine.
// 400 : l'appel n'est pas authentifie, ou le fichier doit etre rechiffre et
// depasse un bloc (voir la limite ci-dessous).
}
}
Droits requis. Vous devez pouvoir lire le fichier source : en être propriétaire, ou bénéficier d'une délégation active de son propriétaire. C'est le même droit que pour le télécharger. Un identifiant qui ne vous est pas accessible reçoit la même réponse qu'un identifiant inexistant, afin de ne pas révéler l'existence des fichiers d'autrui. Le brouillon qui reçoit le fichier doit être le vôtre, ou celui d'un compte qui vous a délégué son accès, et ne pas être déjà terminé.
Une limite à connaître. Si votre société utilise l'option HSM, ou si l'envoi est protégé par un mot de passe, le fichier doit être rechiffré : il est alors relu et déposé en une seule fois, ce qui le limite à un bloc de 8 Mo. Au-delà, l'appel est refusé — passez dans ce cas par le dépôt d'octets classique décrit plus haut.
try
{
var request = createRequest("https://ext.transfertpro.com/api/v5/Inbox/NotDownloadedFilesNumber", "application/json", HttpMethod.Get);
using (HttpResponseMessage response = GetHttpClient().SendAsync(request).Result)
{
if(response.IsSuccessStatusCode)
{
var data = JsonConvert.DeserializeObject<dynamic>(response.Content.ReadAsStringAsync().Result);
int fileNumber = data.Count;
}
else
{
// Gérer les codes status 400, 401, 403...
}
}
request.Dispose();
}
catch (Exception ex)
{
// Gérer les exceptions
}
try
{
string url = string.Format("https://ext.transfertpro.com/api/v5/Inbox/{0}/CopyToWorkspace", fileToCopyToWorkspaceId);
var model = new { targetParentDirectoryId = folderToCopyTargetId };
using (HttpResponseMessage response = GetHttpClient().PutAsJsonAsync(url, model).Result)
{
if(response.IsSuccessStatusCode)
{
// Copie effectuée avec succès.
}
else
{
// Gérer les codes status 400, 401, 403...
}
}
}
catch (Exception ex)
{
// Gérer les exceptions
}
try
{
string searchText; // Texte à rechercher dans le nom du fichier.
var url = string.Format("https://ext.transfertpro.com/api/v5/Inbox/Search/{0}", searchText);
var request = createRequest(url, "application/json", HttpMethod.Get);
using (HttpResponseMessage response = GetHttpClient().SendAsync(request).Result)
{
if(response.IsSuccessStatusCode)
{
// Recherche effectuée avec succès.
List<dynamic> datas = (List<dynamic>)JsonConvert.DeserializeObject(response.Content.ReadAsStringAsync().Result, typeof(List<dynamic>));
}
else
{
// Gérer les codes status 400, 401, 403...
}
}
request.Dispose();
}
catch (Exception ex)
{
// Gérer les exceptions
}