2016-04-12 9 views
7

Sto tentando di caricare il file e quindi leggerlo, tutto funziona correttamente, ma non quando ho inserito l'annotazione @Async nel metodo del gestore.Caricamento ed elaborazione di file asincroni di primavera

Non voglio che l'utente continui ad aspettare finché non elabora il file. Ma dopo aver inserito questa annotazione ottengo l'eccezione java.lang.IllegalStateException: File has been moved - cannot be read again. Cosa succede e come posso risolvere questo problema? Come ho capito, Spring potrebbe semplicemente cancellare il file perché termina la richiesta-risposta e la ripulisce. Ma non dovrebbe impedirlo @Async?

Esempio di applicazione Primavera Boot:

@SpringBootApplication 
@EnableSwagger2 
@ComponentScan(value = "hello") 
@EnableAsync 
public class Application { 

    public static void main(String[] args) { 
     SpringApplication.run(Application.class, args); 
    } 

    public Docket api() { 
     return new Docket(DocumentationType.SWAGGER_2) 
       .select() 
       .apis(RequestHandlerSelectors.any()) 
       .paths(PathSelectors.regex("/api/.*")) 
       .build(); 
    } 
} 

Upload Controller:

@RestController 
@RequestMapping(value = "/files") 
public class FilesController { 

    @Inject 
    private Upload upload; 

    @RequestMapping(method = RequestMethod.POST) 
    public void addSource(@RequestParam MultipartFile file) throws IOException, InterruptedException { 
     upload.process(file); 
    } 
} 

Upload servizio:

@Component 
public class Upload { 

    @Async 
    public void process(MultipartFile file) throws InterruptedException, IOException { 
     sleep(2000); 
     System.out.println(new String(IOUtils.readFully(file.getInputStream(), -1, false))); 
    } 
} 

Ed ora ho java.io.FileNotFoundException. Non sono sicuro di cosa mi manchi qui. Probabilmente sto facendo qualcosa di sbagliato in quanto non sono riuscito a trovare alcun errore su questo aspetto e penso che questo sia un caso d'uso molto comune.

+0

Si effettua una richiesta di posta o si effettua una richiesta Ajax quando si carica il file? –

+0

Inoltre, hai usato l'annotazione @Async nel metodo di servizio o nel controller? –

+0

È una semplice richiesta POST (tramite swagger). Ho inserito l'annotazione @Async sul metodo di servizio. – bargoras

risposta

10

Non è possibile passare il parametro MultipartFile al metodo @Async nel modo in cui lo si fa.

Quando termina il metodo "addSource", MultipartFile esaurisce l'ambito e la risorsa viene rilasciata. Quindi l'accesso all'interno del tuo metodo "processo" fallirà. Costruisci una specie di condizione di gara in questo modo. Springs DispatcherServlet utilizza StandardServletMultipartResolver.cleanupMultipart per pulire quei file. Metti un punto di interruzione lì per vedere quando questo metodo viene chiamato su retun di addSource (...).

È necessario eseguire le operazioni seguenti: Leggere l'intero file in un buffer all'interno del metodo "addSource". Quindi passare il buffer al metodo "process" e lasciare restituire "addSource".

Quello che chiamate "... elabora il file ..." non sta "elaborando" il file, ma lo legge.

@Async 
public void process(byte[] bs){ 
    System.out.println(new String(bs)); 
    //do some long running processing of bs here 
} 

@RequestMapping(method = RequestMethod.POST) 
public void addSource(@RequestParam MultipartFile file) { 
    upload.process(IOUtils.toByteArray(file)); 
} 
+0

Grazie! A proposito, sto facendo una lunga elaborazione qui, è solo un esempio. – bargoras

+0

Bella spiegazione! Sfortunatamente, tuttavia, la soluzione suggerita è un piccolo difetto: negli ambienti aziendali, spesso ci sono file di dimensioni che non possono permettersi di memorizzare nella memoria in quel modo. Tristemente, 'InputStreamResource' non funzionerà neanche. La tua migliore scommessa, per quanto posso dire finora, è quella di copiare il contenuto in un proprio file temporaneo (ad esempio 'File # createTempFile') che puoi pulire alla fine del thread di elaborazione. – Powerslave

Problemi correlati