2016-04-12 9 views
7

Próbuję przesłać plik, a następnie go odczytać, wszystko działa poprawnie, ale nie po umieszczeniu adnotacji @Async w metodzie programu obsługi.Przesyłanie i przetwarzanie wiosennego pliku asynchronicznego

Nie chcę, aby użytkownik czekał, dopóki nie przetworzy pliku. Ale po umieszczeniu tej adnotacji otrzymuję wyjątek java.lang.IllegalStateException: File has been moved - cannot be read again. Co się dzieje i jak to naprawić? Jak rozumiem, Spring może po prostu wyczyścić plik, ponieważ żądanie-odpowiedź kończy się i czyści to. Ale czy nie powinno to przeszkodzić?

Przykładowa aplikacja Wiosna 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(); 
    } 
} 

Prześlij kontroler:

@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); 
    } 
} 

Załaduj usługi:

@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))); 
    } 
} 

A teraz mam java.io.FileNotFoundException. Nie jestem pewien, czego tu brakuje. Prawdopodobnie robię coś złego, ponieważ nie mogłem znaleźć żadnego błędu i uważam, że jest to bardzo powszechny przypadek użycia.

+0

Czy tworzysz prośbę o wpis lub czy wysyłasz plik z zapytaniem o ajax? –

+0

Czy używałeś również adnotacji @Sync w metodzie usługi lub w kontrolerze? –

+0

Jest to proste żądanie POST (poprzez zboczenie). Wpisuję adnotację @Aync do metody serwisowej. – bargoras

Odpowiedz

10

Nie można przekazać parametru MultipartFile do metody @async w sposób, w jaki to robisz.

Po zakończeniu metody "addSource" plik MultipartFile kończy się, a zasób zostaje zwolniony. Tak więc dostęp do metody "procesu" zakończy się niepowodzeniem. W ten sposób budujesz jakiś rodzaj wyścigu. Sprężyna DispatcherServlet używa standardServletMultipartResolver.cleanupMultipart do czyszczenia tych plików. Umieść tam punkt przerwania, aby zobaczyć, kiedy ta metoda zostanie wywołana przy ponownym uruchomieniu addSource (...).

Powinieneś wykonać następujące czynności: Przeczytaj cały plik w buforze wewnątrz metody "addSource". Następnie przeprowadź bufor do metody "process", a następnie zwróć "addSource".

To, co nazywasz "... przetwarza plik ..." nie "przetwarza" pliku, ale go czyta.

@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

Dziękujemy! Przy okazji robię tu długie przetwarzanie, to tylko przykład. – bargoras

+0

Niezłe wytłumaczenie! Niestety, sugerowane rozwiązanie jest wadliwe: W środowiskach korporacyjnych często masz pliki o rozmiarach, których nie możesz sobie pozwolić na buforowanie do pamięci w ten sposób. Niestety, 'InputStreamResource' również nie działa. Najlepszym rozwiązaniem, o ile mogłem powiedzieć, jest skopiowanie zawartości do własnego pliku tymczasowego (np. 'Plik # createTempFile'), który można wyczyścić na końcu wątku przetwarzania. – Powerslave