11using System ;
22using System . Collections . Generic ;
33using System . IO ;
4+ using System . IO . Compression ;
45using System . Linq ;
56using System . Net . Http ;
7+ using System . Net . Http . Headers ;
8+ using System . Threading ;
69using System . Threading . Tasks ;
710using System . Web . Http ;
811using SmartStore . Core ;
12+ using SmartStore . Core . Domain ;
913using SmartStore . Core . Domain . Catalog ;
14+ using SmartStore . Core . Domain . DataExchange ;
1015using SmartStore . Core . Domain . Media ;
16+ using SmartStore . Core . IO ;
1117using SmartStore . Services . Catalog ;
18+ using SmartStore . Services . DataExchange . Import ;
1219using SmartStore . Services . Media ;
1320using SmartStore . Utilities ;
21+ using SmartStore . Utilities . Threading ;
1422using SmartStore . Web . Framework . WebApi ;
1523using SmartStore . Web . Framework . WebApi . OData ;
1624using SmartStore . Web . Framework . WebApi . Security ;
1725using SmartStore . WebApi . Models . Api ;
1826
1927namespace SmartStore . WebApi . Controllers . Api
2028{
29+ /// <see cref="http://www.asp.net/web-api/overview/advanced/sending-html-form-data,-part-2"/>
2130 public class UploadsController : ApiController
2231 {
32+ private static readonly ReaderWriterLockSlim _rwLock = new ReaderWriterLockSlim ( ) ;
33+
2334 private readonly Lazy < IProductService > _productService ;
2435 private readonly Lazy < IPictureService > _pictureService ;
36+ private readonly Lazy < IImportProfileService > _importProfileService ;
2537 private readonly Lazy < IStoreContext > _storeContext ;
2638 private readonly Lazy < MediaSettings > _mediaSettings ;
2739
2840 public UploadsController (
2941 Lazy < IProductService > productService ,
3042 Lazy < IPictureService > pictureService ,
43+ Lazy < IImportProfileService > importProfileService ,
3144 Lazy < IStoreContext > storeContext ,
3245 Lazy < MediaSettings > mediaSettings )
3346 {
3447 _productService = productService ;
3548 _pictureService = pictureService ;
49+ _importProfileService = importProfileService ;
3650 _storeContext = storeContext ;
3751 _mediaSettings = mediaSettings ;
3852 }
3953
40- /// <see cref="http://www.asp.net/web-api/overview/advanced/sending-html-form-data,-part-2"/>
54+ #region Utilities
55+
56+ private StringContent CloneHeaderContent ( string path , MultipartFileData origin )
57+ {
58+ var content = new StringContent ( path ) ;
59+
60+ ContentDispositionHeaderValue disposition ;
61+ ContentDispositionHeaderValue . TryParse ( origin . Headers . ContentDisposition . ToString ( ) , out disposition ) ;
62+
63+ content . Headers . ContentDisposition = disposition ;
64+
65+ content . Headers . ContentDisposition . Name = origin . Headers . ContentDisposition . Name . ToUnquoted ( ) ;
66+ content . Headers . ContentDisposition . FileName = Path . GetFileName ( path ) ;
67+
68+ content . Headers . ContentType . MediaType = MimeTypes . MapNameToMimeType ( path ) ;
69+
70+ return content ;
71+ }
72+
73+ #endregion
74+
75+ [ HttpPost ]
4176 [ WebApiAuthenticate ( Permission = "ManageCatalog" ) ]
4277 [ WebApiQueryable ( PagingOptional = true ) ]
43- public async Task < IQueryable < UploadImage > > PostProductImages ( )
78+ public async Task < IQueryable < UploadImage > > ProductImages ( )
4479 {
4580 if ( ! Request . Content . IsMimeMultipartContent ( ) )
4681 {
@@ -56,10 +91,10 @@ public async Task<IQueryable<UploadImage>> PostProductImages()
5691 {
5792 await Request . Content . ReadAsMultipartAsync ( provider ) ;
5893 }
59- catch ( Exception exc )
94+ catch ( Exception exception )
6095 {
6196 provider . DeleteLocalFiles ( ) ;
62- throw this . ExceptionInternalServerError ( exc ) ;
97+ throw this . ExceptionInternalServerError ( exception ) ;
6398 }
6499
65100 // find product entity
@@ -82,7 +117,7 @@ public async Task<IQueryable<UploadImage>> PostProductImages()
82117 if ( entity == null )
83118 {
84119 provider . DeleteLocalFiles ( ) ;
85- throw this . ExceptionNotFound ( WebApiGlobal . Error . EntityNotFound . FormatWith ( identifier . NaIfEmpty ( ) ) ) ;
120+ throw this . ExceptionNotFound ( WebApiGlobal . Error . EntityNotFound . FormatInvariant ( identifier . NaIfEmpty ( ) ) ) ;
86121 }
87122
88123 // process images
@@ -97,15 +132,7 @@ public async Task<IQueryable<UploadImage>> PostProductImages()
97132
98133 foreach ( var file in provider . FileData )
99134 {
100- var image = new UploadImage
101- {
102- FileName = file . Headers . ContentDisposition . FileName . ToUnquoted ( ) ,
103- Name = file . Headers . ContentDisposition . Name . ToUnquoted ( ) ,
104- ContentDisposition = file . Headers . ContentDisposition . Parameters
105- } ;
106-
107- if ( file . Headers . ContentType != null )
108- image . MediaType = file . Headers . ContentType . MediaType . ToUnquoted ( ) ;
135+ var image = new UploadImage ( file . Headers ) ;
109136
110137 if ( image . FileName . IsEmpty ( ) )
111138 image . FileName = entity . Name ;
@@ -115,7 +142,6 @@ public async Task<IQueryable<UploadImage>> PostProductImages()
115142 if ( pictureBinary != null && pictureBinary . Length > 0 )
116143 {
117144 pictureBinary = _pictureService . Value . ValidatePicture ( pictureBinary ) ;
118-
119145 pictureBinary = _pictureService . Value . FindEqualPicture ( pictureBinary , pictures , out equalPictureId ) ;
120146
121147 if ( pictureBinary != null )
@@ -157,5 +183,121 @@ public async Task<IQueryable<UploadImage>> PostProductImages()
157183 provider . DeleteLocalFiles ( ) ;
158184 return result . AsQueryable ( ) ;
159185 }
186+
187+ [ HttpPost ]
188+ [ WebApiAuthenticate ( Permission = "ManageImports" ) ]
189+ [ WebApiQueryable ( PagingOptional = true ) ]
190+ public async Task < IQueryable < UploadImportFile > > ImportFiles ( )
191+ {
192+ if ( ! Request . Content . IsMimeMultipartContent ( ) )
193+ {
194+ throw this . ExceptionUnsupportedMediaType ( ) ;
195+ }
196+
197+ ImportProfile profile = null ;
198+ string identifier = null ;
199+ var tempDir = FileSystemHelper . TempDir ( Guid . NewGuid ( ) . ToString ( ) ) ;
200+ var provider = new MultipartFormDataStreamProvider ( tempDir ) ;
201+
202+ try
203+ {
204+ await Request . Content . ReadAsMultipartAsync ( provider ) ;
205+ }
206+ catch ( Exception exception )
207+ {
208+ FileSystemHelper . ClearDirectory ( tempDir , true ) ;
209+ throw this . ExceptionInternalServerError ( exception ) ;
210+ }
211+
212+ // find import profile
213+ if ( provider . FormData . AllKeys . Contains ( "Id" ) )
214+ {
215+ identifier = provider . FormData . GetValues ( "Id" ) . FirstOrDefault ( ) ;
216+ profile = _importProfileService . Value . GetImportProfileById ( identifier . ToInt ( ) ) ;
217+ }
218+ else if ( provider . FormData . AllKeys . Contains ( "Name" ) )
219+ {
220+ identifier = provider . FormData . GetValues ( "Name" ) . FirstOrDefault ( ) ;
221+ profile = _importProfileService . Value . GetImportProfileByName ( identifier ) ;
222+ }
223+
224+ if ( profile == null )
225+ {
226+ FileSystemHelper . ClearDirectory ( tempDir , true ) ;
227+ throw this . ExceptionNotFound ( WebApiGlobal . Error . EntityNotFound . FormatInvariant ( identifier . NaIfEmpty ( ) ) ) ;
228+ }
229+
230+ var deleteExisting = false ;
231+ var result = new List < UploadImportFile > ( ) ;
232+ var unzippedFiles = new List < MultipartFileData > ( ) ;
233+ var importFolder = profile . GetImportFolder ( true , true ) ;
234+ var csvTypes = new string [ ] { ".csv" , ".txt" , ".tab" } ;
235+
236+ if ( provider . FormData . AllKeys . Contains ( "deleteExisting" ) )
237+ {
238+ var strDeleteExisting = provider . FormData . GetValues ( "deleteExisting" ) . FirstOrDefault ( ) ;
239+ deleteExisting = ( strDeleteExisting . HasValue ( ) && strDeleteExisting . ToBool ( ) ) ;
240+ }
241+
242+ // unzip files
243+ foreach ( var file in provider . FileData )
244+ {
245+ var import = new UploadImportFile ( file . Headers ) ;
246+
247+ if ( import . FileExtension . IsCaseInsensitiveEqual ( ".zip" ) )
248+ {
249+ var subDir = Path . Combine ( tempDir , Guid . NewGuid ( ) . ToString ( ) ) ;
250+ ZipFile . ExtractToDirectory ( file . LocalFileName , subDir ) ;
251+ FileSystemHelper . Delete ( file . LocalFileName ) ;
252+
253+ foreach ( var unzippedFile in Directory . GetFiles ( subDir , "*.*" ) )
254+ {
255+ var content = CloneHeaderContent ( unzippedFile , file ) ;
256+ unzippedFiles . Add ( new MultipartFileData ( content . Headers , unzippedFile ) ) ;
257+ }
258+ }
259+ else
260+ {
261+ unzippedFiles . Add ( new MultipartFileData ( file . Headers , file . LocalFileName ) ) ;
262+ }
263+ }
264+
265+ // copy files to import folder
266+ if ( unzippedFiles . Any ( ) )
267+ {
268+ using ( _rwLock . GetWriteLock ( ) )
269+ {
270+ if ( deleteExisting )
271+ {
272+ FileSystemHelper . ClearDirectory ( importFolder , false ) ;
273+ }
274+
275+ foreach ( var file in unzippedFiles )
276+ {
277+ var import = new UploadImportFile ( file . Headers ) ;
278+ var destPath = Path . Combine ( importFolder , import . FileName ) ;
279+
280+ import . Exists = File . Exists ( destPath ) ;
281+
282+ switch ( profile . FileType )
283+ {
284+ case ImportFileType . XLSX :
285+ import . IsSupportedByProfile = import . FileExtension . IsCaseInsensitiveEqual ( ".xlsx" ) ;
286+ break ;
287+ case ImportFileType . CSV :
288+ import . IsSupportedByProfile = csvTypes . Contains ( import . FileExtension , StringComparer . OrdinalIgnoreCase ) ;
289+ break ;
290+ }
291+
292+ import . Inserted = FileSystemHelper . Copy ( file . LocalFileName , destPath ) ;
293+
294+ result . Add ( import ) ;
295+ }
296+ }
297+ }
298+
299+ FileSystemHelper . ClearDirectory ( tempDir , true ) ;
300+ return result . AsQueryable ( ) ;
301+ }
160302 }
161303}
0 commit comments