Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

[.NET] Bestand op server vergelijken met lijstje bestanden

Pagina: 1
Acties:

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 18-11 13:07
Hoi,

Ik zit met een design kwestie waar jullie wellicht bij zouden kunnen helpen. Ik heb al een tijdje een website draaien waar gebruikers een bepaald type bestand kunnen uploaden. Het gaat hier om 'setup' bestanden van race auto's voor een race spel (de afstelling van de auto dus). Bij het uploaden geven de gebruikers wat informatie over het bestand (zoals de auto en race track waarvoor het dient, de rondetijd die ermee gereden is, wie de afstelling gemaakt heeft, etc). Voor elk bestand wordt een unieke GUID gegenereerd bij het uploaden, en komt er een entry in mijn database met alle informatie (guid, auto, track, etc). Het bestand krijgt de GUID als naam en komt in een aparte map op de server te staan. De website toont dan een lijst met alle entries in de database, en wanneer een gebruiker een setup bestand wil downloaden kan de server simpelweg het bestand opzoeken via de GUID.


Naast deze website ben ik nu een Windows client aan het schrijven (WPF, C#), waarvan de bedoeling is dat hij periodiek alle nieuwe setup bestanden automatisch download. De gebruiker kan dan bijvoorbeeld een subscription nemen op een bepaalde auto, en periodiek worden dan alle setups van die auto opgehaald en gedownload. De communicatie tussen de client en de webserver verloopt simpelweg via een ASP.NET webservice.

De client gebruikt in eerste instantie enkel de entries uit de database, zodat de gebruiker het lijstje met setups en hun informatie te zien krijgt (maar nog niet meteen de bestanden zelf). Het downloaden verloopt dan via een aparte webmethod waar de GUID van een setup naartoe gestuurd wordt, waarna op de server het bestand ingelezen wordt en als bytes terug gestuurd wordt.

Een probleem is nu echter het automatisch downloaden van nieuwe setups. Op de een of andere manier moet ik bij zien te houden welke bestanden de gebruiker al (al dan niet automatisch) gedownload heeft, en welke bestanden nieuw zijn. Ik kan wel een aantal manieren bedenken maar alles heeft vrij grote nadelen.

1. De eerste optie was vrij simpel, namelijk op de client bijhouden wanneer de laatste 'synchronizatie' (automatische download) geweest is. Elke setup die daarna geupload wordt is nieuw en moet gedownload worden, en elke setup die voor die datum al bestond is oud en heeft de gebruiker al. Dit is vrij simpel te implementeren maar heeft duidelijk nadelen: de gebruiker kan de bestanden natuurlijk verwijderen (of stel hij gebruikt een nieuwe pc) waarna de client ze niet meer opnieuw zal downloaden.

2. Als tweede optie zou ik in de database (op de server) kunnen bijhouden welke gebruiker welke setup gedownload heeft. Dat doe ik momenteel al (met een extra boolean om aan te geven of de setup automatisch via de client gedownload is of gewoon handmatig via de website), maar ook hierbij geldt hetzelfde nadeel, ik kan niet controleren of het bestand nog steeds bestaat. Eigenlijk komt het erop neer dat ik alleen kan zien of een gebruiker een bestand download, maar ik kan daarna niet meer controleren wat hij er mee doet.

3. Als laatste optie had ik bedacht om een hash van de filecontents te maken. Elke keer als een nieuwe setup op de website geupload wordt hash ik de inhoudt (met MD5, dat is snel voor zover ik weet) en sla ik die ook op in de database. In die client kan ik nu van elk setup bestand ook een hash maken (de locatie van de bestanden is altijd een vaste plek), en zo weet ik precies welke setups er al op de pc van de gebruiker staan en welke nog niet. Zo zou ik alleen de setups kunnen laten tonen die de gebruiker nog niet heeft. Ik ga even voor het gemak er van uit dat de kans op conflicts verwaarloosbaar is en dat de bestanden echt hetzelfde zijn als de hash hetzelfde is.

Optie 3 vind ik zelf de mooiste maar heeft ook weer een groot nadeel: bij het opstarten van de client moet hij nu elke setup gaan inlezen en hashen, en ik had eigenlijk gehoopt dat dat iets sneller zou gaan maar het valt me behoorlijk tegen. Ik neem aan de MD5 hashen van deze bestanden (het gaat om < 5 kB per bestand) heel snel gaat, ik denk dat het inlezen van de bestanden de meeste tijd kost.

Ik gebruik de volgende method om alle .sto files in een bepaalde directory (en subdirectories) te hashen:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
        public static void ComputeHashes()
        {
            _fileHashes = new Dictionary<byte[], string>();

            using (var md5 = MD5.Create())
            foreach (var file in Directory.GetFiles(_setupsPath, "*.sto", SearchOption.AllDirectories))
            {
                var contents = File.ReadAllBytes(file);
                var hash = md5.ComputeHash(contents);

                _fileHashes.Add(hash, file);
            }
        }


Het gaat in mijn geval om 1240 bestanden, wat volgens mij een relatief klein aantal bestanden is om te hashen, maar toch duurt het > 15 seconden, terwijl mijn harde schijf flink zit te ratelen.

Zijn er slimmere manieren om een dergelijk aantal bestanden te kunnen vergelijken met een bestand op de server, zonder meteen het hele bestand naar de client te downloaden?

Weten jullie misschien betere manieren waarop ik kan checken of een bestand (op de server) al bestaat op de client?

Bedankt!

Mijn iRacing profiel


  • Brainstorm
  • Registratie: November 2000
  • Laatst online: 16-11 18:53
Aangezien bestanden altijd (?) via de server geupload worden, kun je gebruik maken van versie nummering per bestand. Bij iedere upload laat je de server bepalen:
* Is het een nieuwe file, dan geef je deze versienummer '1'
* is het een bestaande file, dan hoog je het versienummer op.

Bij het downloaden naar de client stuur je ook het versienummer door. Kijken of een client de laatst versie heeft kan door de client & server een lijst met bestand guids + versie nummers uit te laten wisselen en te vergelijken.

Als extra maatregel kun je dan ook nog hashes gebruiken (optioneel):
* Server side gebruik je hashing om te bepalen of het bestand veranderd is (en je dus het versienummer moet ophogen)
* Client side gebruik je hashing om na iedere download te controleren of het ontvangen bestand overeen komt met wat je ontvangen zou moeten hebben.

Programmer's Drinking Song: 99 little bugs in the code, 99 bugs in the code, Fix one bug, compile it again, 100 little bugs in the code. (go to start if bugs>0)


  • Caelorum
  • Registratie: April 2005
  • Laatst online: 00:38
NickThissen schreef op zondag 25 november 2012 @ 11:13:
[...]Optie 3 vind ik zelf de mooiste maar heeft ook weer een groot nadeel: bij het opstarten van de client moet hij nu elke setup gaan inlezen en hashen, en ik had eigenlijk gehoopt dat dat iets sneller zou gaan maar het valt me behoorlijk tegen. [...]
Waarom niet lokaal op de client nog bijhouden welke bestanden al zijn gehasht? Dan hoef je die ook niet meer opnieuw in te lezen en te hashen. Met FileSystemInfo.LastWriteTime kan je dan bijhouden of het bestand is verandert sinds de laatste keer dat je er een hash van hebt gemaakt.
Dan heb je alleen nog de eerste keer dat de client langzaam opstart. Elke opvolgende keer hoort sneller te zijn (tenzij alle setup bestanden zijn verandert natuurlijk).

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 18-11 13:07
Brainstorm schreef op zondag 25 november 2012 @ 11:29:
Aangezien bestanden altijd (?) via de server geupload worden, kun je gebruik maken van versie nummering per bestand. Bij iedere upload laat je de server bepalen:
* Is het een nieuwe file, dan geef je deze versienummer '1'
* is het een bestaande file, dan hoog je het versienummer op.

Bij het downloaden naar de client stuur je ook het versienummer door. Kijken of een client de laatst versie heeft kan door de client & server een lijst met bestand guids + versie nummers uit te laten wisselen en te vergelijken.
Misschien begrijp ik je verkeerd, maar volgens mij gaat dit niet werken. Het gaat om meerdere gebruikers, in principe kan iedere gebruiker een setup uploaden (momenteel alleen via de website, straks misschien ook via de client), en elke andere gebruiker kan die weer downloaden. Of het een "nieuwe file" is hangt af van de gebruiker, als gebruiker 1 hem download is het voor hem geen nieuwe file meer, maar wellicht voor gebruiker 2 wel...

Het gebruik van versienummering is wellicht wel handig voor het geval een gebruiker zijn setup aanpast (dit is via de site mogelijk, hij kan de informatie van de setup aanpassen en ook een nieuw bestand uploaden onder dezelfde GUID), maar dat is eigenlijk de volgende stap en heeft niet zoveel met dit probleem te maken.
Caelorum schreef op zondag 25 november 2012 @ 11:38:
[...]

Waarom niet lokaal op de client nog bijhouden welke bestanden al zijn gehasht? Dan hoef je die ook niet meer opnieuw in te lezen en te hashen. Met FileSystemInfo.LastWriteTime kan je dan bijhouden of het bestand is verandert sinds de laatste keer dat je er een hash van hebt gemaakt.
Dan heb je alleen nog de eerste keer dat de client langzaam opstart. Elke opvolgende keer hoort sneller te zijn (tenzij alle setup bestanden zijn verandert natuurlijk).
Maar dan zit ik weer met een vergelijkbaar probleem dat ik in optie 1 en 2 ook had: de gebruiker kan de setup bestanden geheel los van de client veranderen, verwijderen, hernoemen, etc. Voor zover ik weet kan ik dat allemaal niet bijhouden (tenminste niet wanneer de client niet opgestart is). Misschien is dat geen ramp, maar het komt er dus op neer dat ik een setup opnieuw ga hashen als de naam veranderd is en als de contents veranderd zijn. Dat zal niet heel vaak gebeuren dus misschien valt het mee. Hier ga ik maar eens mee aan de slag denk ik...

Bedankt :)

Mijn iRacing profiel


  • Gomez12
  • Registratie: Maart 2001
  • Laatst online: 17-10-2023
Wat is het kritieke punt nou eigenlijk bij 1of 2?
Ik bedoel je kan toch toch in een backgroundthread op een low prio checken of die files er lokaal nog zijn.

  • Patriot
  • Registratie: December 2004
  • Laatst online: 00:34

Patriot

Fulltime #whatpulsert

Ik vraag me eigenlijk af waarom je überhaupt het probleem van een gebruiker die gaat verwijderen/renamen op wilt vangen. Ik kan niet zeggen dat je het niet zou moeten doen, want daarvoor weet ik te weinig van de situatie, maar is het echt iets waar je het programma tegen moet beschermen?

EDIT:
Het probleem is verder ook dat jij gewoon echt alles moet gaan vergelijken, als het systeem zo robuust moet. Je zou eventueel fracties van bestanden kunnen gaan hashen (want de I/O is duidelijk de bottleneck hier), maar dan loop je het risico op collisions. Sterker nog, als sommige setups sterk op elkaar lijken (en daardoor bijvoorbeeld alleen in de laatste paar kB verschillen) ontkom je er niet aan om volledige vergelijkingen te doen en dan loop je weer tegen je HDD aan.

[ Voor 46% gewijzigd door Patriot op 26-11-2012 01:38 ]


  • Schnoop
  • Registratie: Juli 2006
  • Laatst online: 24-09 21:03
Je kunt toch ook gewoon op de client via xml/ini/txt of op een andere manier een lijst bijhouden van bestanden + datum laatst gedownloaden. Als server tijd van de file nieuwer is doe je hem opnieuw downloaden.

En om het "gebruiker verwijderd het bestand" tegen te gaan doe je altijd controleren of de files in je xml/ini/txt bestand nog bestaan en zo niet opnieuw downloaden.

  • Hydra
  • Registratie: September 2000
  • Laatst online: 06-10 13:59
Geef de user de optie om alles opnieuw te downloaden en je kunt prima voor optie 1 gaan m.i. De enige andere optie is om regelmatig een lijst met alle files naar de client te sturen waarna de client (bijvoorbeeld aan de hand van een hash) kan kijken of hij deze al heeft.

https://niels.nu


  • Patriot
  • Registratie: December 2004
  • Laatst online: 00:34

Patriot

Fulltime #whatpulsert

Schnoop schreef op maandag 26 november 2012 @ 07:50:
Je kunt toch ook gewoon op de client via xml/ini/txt of op een andere manier een lijst bijhouden van bestanden + datum laatst gedownloaden. Als server tijd van de file nieuwer is doe je hem opnieuw downloaden.

En om het "gebruiker verwijderd het bestand" tegen te gaan doe je altijd controleren of de files in je xml/ini/txt bestand nog bestaan en zo niet opnieuw downloaden.
En wat als ze de bestandsnaam veranderen?
Hydra schreef op maandag 26 november 2012 @ 09:46:
Geef de user de optie om alles opnieuw te downloaden en je kunt prima voor optie 1 gaan m.i. De enige andere optie is om regelmatig een lijst met alle files naar de client te sturen waarna de client (bijvoorbeeld aan de hand van een hash) kan kijken of hij deze al heeft.
Dan ben je natuurlijk nog steeds 15 seconden aan het hashen.

  • Gomez12
  • Registratie: Maart 2001
  • Laatst online: 17-10-2023
Patriot schreef op maandag 26 november 2012 @ 20:04:
[...]
Dan ben je natuurlijk nog steeds 15 seconden aan het hashen.
Wat is het probleem met die 15 seconden aan het hashen zijn?

Doe het in de background en je merkt er niets van, verdeel het op in max 100 bestanden per opstart en je hebt het over 1 seconde background tijd. Creeer een knop zodat de gebruiker het zelf opstart etc. etc.

Als ik het goed begrijp is het probleem enkel met de huidige implementatie dat het bij elke opstart 15 seconden duurt om het programma op te starten omdat het hashen in de main thread gebeurt. Haal het uit de main thread en het directe probleem is weg.

Daarnaast kan je uiteraard gaan kijken naar snellere/efficientere manieren om te gaan hashen etc. Maar haal eerst het pijnpunt weg.

Sowieso zie ik het hele probleem niet, maargoed. Hanteer een interne lijst met hashes / filedatums etc zodat je snel kan scannen of alles wat gehashed is nog ok is, zoniet dan dat bestand herhashen. Elk in te laden bestand wordt gehashed en vergelijken met die lijst.
Dan heb je volgens mij verwijderingen / vervangingen ondervangen zonder elke keer te hashen, maar opzich zou ik gewoon simpelweg alles hashen mocht dat gewenst zijn (al die optimalisaties zijn enkel foutgevoelig) en kijken wat er fout gaat bij je hashfuncties.

Er gaat met die snelheid simpelweg iets fout in je manier van hashen. Als ik 2500 bestanden pak dan kost dat bij mij <1 seconde om totaal te hashen.

  • edeboeck
  • Registratie: Maart 2005
  • Laatst online: 20-11 12:23

edeboeck

mie noow noooothing ...

Ligt het aan mij of wordt het hier nodeloos ingewikkeld gemaakt?
NickThissen schreef op zondag 25 november 2012 @ 11:13:
(...)
Het bestand krijgt de GUID als naam en komt in een aparte map op de server te staan.
(...)
Een probleem is nu echter het automatisch downloaden van nieuwe setups. Op de een of andere manier moet ik bij zien te houden welke bestanden de gebruiker al (al dan niet automatisch) gedownload heeft, en welke bestanden nieuw zijn. Ik kan wel een aantal manieren bedenken maar alles heeft vrij grote nadelen.
(...)
Weten jullie misschien betere manieren waarop ik kan checken of een bestand (op de server) al bestaat op de client?
Aangezien je een Windows-client gebruikt, kan die toch perfect (de betrokken directory in) het filesystem aflopen, daar de GUIDs verzamelen en die meezenden naar de server?

Of mis ik ergens iets? :?
Pagina: 1